diff --git a/VERSIONS_WIP/111127 b/VERSIONS_WIP/111127
new file mode 100644
index 0000000000000000000000000000000000000000..a2f406ad2b5dc4d48f80bafac0c87d52113716bc
--- /dev/null
+++ b/VERSIONS_WIP/111127
@@ -0,0 +1,10 @@
+ - ticket #111127 : Drive :
+       - administration : ajout des colonnes Section et Emplacement dans l'export des documents
+       - administration : possibilité de visualiser plusieurs journées de rendez-vous
+       - confirmation de rendez-vous : utilisation du mail renseigné dans la fiche bibliothèque en priorité
+       - popup de connexion : n'affiche le bouton "planifier le retrait de mes documents" seulement si un document en attente de retrait dans une bibliothèque avec drive activé
+       - prise de rendez-vous : s'il n'y a qu'un site de retrait possible l'abonné est dirigé directement sur le choix d'une date pour ce site
+       - prise de rendez-vous : nouvelle variable "DRIVE_DISABLE_HOLD_CHECK" permettant la prise de rendez-vous sans vérification de l'existence ou de l'état des réservations de l'abonné
+       - prise de rendez-vous : modification du libellé de suppression pour "Annuler ou replanifier", ajout de détail dans le message de confirmation de suppression
+       - prise de rendez-vous : ajout d'une notification par mail lors de la suppression
+       - prise de rendez-vous : enregistrement de la date de création du rendez-vous qui est affichée en administration et présentée dans les exports csv
\ No newline at end of file
diff --git a/application/modules/admin/controllers/DriveCheckoutController.php b/application/modules/admin/controllers/DriveCheckoutController.php
index 161a15f2df4033b1ee105d30ebcdbff401cc1156..ea2a7003fbed955f69ed341e8eeafe96b1f0743a 100644
--- a/application/modules/admin/controllers/DriveCheckoutController.php
+++ b/application/modules/admin/controllers/DriveCheckoutController.php
@@ -35,13 +35,13 @@ class Admin_DriveCheckoutController extends ZendAfi_Controller_Action {
   public function listAction() {
     $this->view->library = Class_Bib::find($this->_getParam('id_bib'));
     $this->view->date = $this->_getParam('date', $this->getCurrentDate());
+    $this->view->days = (int)$this->_getParam('days', 1);
     $this->view->titre = $this->view->_('Drive : rendez-vous : %s, %s',
                                         $this->view->library->getLibelle(),
                                         strftime('%d %B %Y', strtotime($this->view->date)));
-    $this->view->checkouts = Class_DriveCheckout::findAllBy(['role' => 'library',
-                                                             'model' => $this->view->library,
-                                                             'left(start_at,10)' => $this->view->date,
-                                                             'order' => 'start_at']);
+    $this->view->checkouts = Class_DriveCheckout::findAllByLibraryAndDates($this->view->library,
+                                                                           $this->view->date,
+                                                                           $this->view->days);
     $this->view->table_description = new Class_TableDescription_DriveCheckout_ListWithActions('checkouts');
   }
 
diff --git a/application/modules/admin/controllers/SessionActivityController.php b/application/modules/admin/controllers/SessionActivityController.php
index b93a06c232806d94c6d2e550abb5081574b5a39a..f02b6bc5886d0152facf6b0eae36f3b67dfa48f5 100644
--- a/application/modules/admin/controllers/SessionActivityController.php
+++ b/application/modules/admin/controllers/SessionActivityController.php
@@ -308,6 +308,8 @@ class SessionOneLetterPerDayFusionStrategy extends AbstractSessionFusionStrategy
 
 
 class FusionDateContext {
+  use Trait_GetterByAttributeName;
+
   protected $_current_date;
 
   public function __construct($datestr=null) {
@@ -339,11 +341,6 @@ class FusionDateContext {
     $other_date->sub($this->_current_date);
     return ($other_date->toValue(Zend_Date::DAY)-1);
   }
-
-
-  public function callGetterByAttributeName($attribute) {
-    return call_user_func(array($this, 'get'.Storm_Inflector::camelize($attribute)));
-  }
 }
 
 
diff --git a/application/modules/admin/views/scripts/drive-checkout/list.phtml b/application/modules/admin/views/scripts/drive-checkout/list.phtml
index 89ac4e7de436abe227d087420b9cbb61c0ab965a..1a6acb73cf876f83e370166ddd64f286ad09935a 100644
--- a/application/modules/admin/views/scripts/drive-checkout/list.phtml
+++ b/application/modules/admin/views/scripts/drive-checkout/list.phtml
@@ -16,12 +16,25 @@ echo $this->Button((new Class_Entity())
                    ->setAttribs(['style' => 'float:right']));
 
 
-echo $this->tag('label', $this->_('Date'), ['for' => 'date', 'style' => 'margin-right: 5px']);
-
 $date_url = $this->url(['date' => null]) . '/date/';
-echo $this->formDate('date',
-                     $this->date,
-                     ['onchange' => 'window.location=\'' . $date_url . '\' + this.value']);
+$days_url = $this->url(['days' => null]) . '/days/';
+
+echo $this
+  ->tag('form',
+        $this->tag('label', $this->_('Date'),
+                   ['for' => 'date', 'style' => 'margin-right: 5px'])
+        . $this->formDate('date',
+                          $this->date,
+                          ['onchange' => 'window.location=\'' . $date_url . '\' + this.value'])
+        . BR
+        . $this->tag('label', $this->_('Nombre de jours à afficher'),
+                     ['for' => 'days', 'style' => 'margin-right: 5px;'])
+        . $this->formNumber('days',
+                            $this->days,
+                            ['onchange' => 'window.location=\'' . $days_url . '\' + this.value',
+                             'size' => 2])
+  );
 
 
-echo $this->renderTable($this->table_description, $this->checkouts);
+echo $this->renderTable($this->table_description,
+                        (new Class_TableDescription_Models($this->checkouts))->groupBy('day_label'));
diff --git a/application/modules/opac/controllers/DriveCheckoutController.php b/application/modules/opac/controllers/DriveCheckoutController.php
index 334fe48d4f3b4e67a43d5b0eb4aacd4bfea8b41a..3088d7be848511c26c5615387b2289fdf2d5c722 100644
--- a/application/modules/opac/controllers/DriveCheckoutController.php
+++ b/application/modules/opac/controllers/DriveCheckoutController.php
@@ -42,6 +42,12 @@ class DriveCheckoutController extends ZendAfi_Controller_Action {
       return;
     }
 
+    if ($library = $plan->onlyPossibleLibrary()) {
+      $this->getHelper('ViewRenderer')->setNoRender();
+      $this->_redirect($this->view->url($plan->libraryUrlFor($library)));
+      return;
+    }
+
     if ($this->_request->isPost()
         && ($checkout = $plan->persist())) {
       $this->getHelper('ViewRenderer')->setNoRender();
@@ -64,6 +70,7 @@ class DriveCheckoutController extends ZendAfi_Controller_Action {
     $message = $this->_('Impossible de supprimer un retrait inconnu.');
     if ($checkout = Class_DriveCheckout::findFor($this->_getParam('id'), $this->_user)) {
       $checkout->delete();
+      $checkout->notifydelete();
       $message = $this->_('Le retrait de vos documents de %s planifié pour %s a été supprimé.',
                           $checkout->getLibraryLabel(),
                           $checkout->getDateTimeLabel());
diff --git a/cosmogramme/sql/patch/patch_390.php b/cosmogramme/sql/patch/patch_390.php
new file mode 100644
index 0000000000000000000000000000000000000000..f1fa358912d1d1b35feccca3d7d6ab35d1b14df0
--- /dev/null
+++ b/cosmogramme/sql/patch/patch_390.php
@@ -0,0 +1,6 @@
+<?php
+$adapter = Zend_Db_Table::getDefaultAdapter();
+
+try {
+  $adapter->query('alter table drive_checkout add created_at datetime not null');
+} catch (Exception $e) {}
diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php
index c8d2f8c801157bf0abfa9cd19bcbd3f33a7b130a..d635d59fd253a601e14b0d39c836ed588f83d904 100644
--- a/library/Class/AdminVar.php
+++ b/library/Class/AdminVar.php
@@ -554,10 +554,15 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
   protected function _getDriveCheckoutVars() {
     return
       ['ENABLE_DRIVE_CHECKOUT' => Class_AdminVar_Meta::newOnOff($this->_('Activer la prise de rendez-vous pour récupérer des réservations (Drive)')),
+       'DRIVE_DISABLE_HOLD_CHECK' => Class_AdminVar_Meta::newOnOff($this->_('Permettre la prise de rendez-vous sans vérification de l\'existence ou de l\'état des réservations de l\'abonné')),
        'DRIVE_TEMPLATE_NEW_RDV_SUBJECT' => Class_AdminVar_Meta::newDefault($this->_('Sujet des courriels de confirmation de création de rendez-vous drive.'),
                                                                            ['value'=> 'Confirmation de rendez-vous à {library.libelle}']),
        'DRIVE_TEMPLATE_NEW_RDV_CONTENT' => Class_AdminVar_Meta::newEditor($this->_('Modèle utilisé pour les courriels de confirmation de création de rendez-vous drive.'),
                                                                           ['value'=> '<p>Bonjour {user.nom_complet},</p> <p>nous vous confirmons votre rendez-vous <strong> &agrave; {library.libelle} {rendez_vous.date_time_label}</strong>.</p>']),
+       'DRIVE_TEMPLATE_DELETE_RDV_SUBJECT' => Class_AdminVar_Meta::newDefault($this->_('Sujet des courriels de suppression de rendez-vous drive.'),
+                                                                           ['value'=> 'Suppression de votre rendez-vous à {library.libelle}']),
+       'DRIVE_TEMPLATE_DELETE_RDV_CONTENT' => Class_AdminVar_Meta::newEditor($this->_('Modèle utilisé pour les courriels de suppression de rendez-vous drive.'),
+                                                                          ['value'=> '<p>Bonjour {user.nom_complet},</p> <p>nous vous confirmons la suppression de votre rendez-vous <strong> &agrave; {library.libelle} {rendez_vous.date_time_label}</strong>.</p>']),
       ];
   }
 
@@ -1148,6 +1153,11 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
   public function isDriveCheckoutEnabled() {
     return Class_AdminVar::isModuleEnabled('ENABLE_DRIVE_CHECKOUT');
   }
+
+
+  public function isDriveCheckoutShouldFilterLibraries() {
+    return !Class_AdminVar::isModuleEnabled('DRIVE_DISABLE_HOLD_CHECK');
+  }
 }
 
 
diff --git a/library/Class/DriveCheckout.php b/library/Class/DriveCheckout.php
index 0526f2d36d6bbcb8492a682def826156a6faefcb..754b326a6861f4d159241a379be2468c1f70ccf8 100644
--- a/library/Class/DriveCheckout.php
+++ b/library/Class/DriveCheckout.php
@@ -31,6 +31,20 @@ class Class_DriveCheckoutLoader extends Storm_Model_Loader {
   }
 
 
+  public function findAllByLibraryAndDates($library, $start_date, $days_count) {
+    $checkouts = new Storm_Model_Collection();
+    for($i=0; $i<$days_count; $i++) {
+      $start_at = strftime('%Y-%m-%d', strtotime($start_date . ' +' . $i . ' day'));
+      $checkouts
+        ->addAll(Class_DriveCheckout::findAllBy(['role' => 'library',
+                                                 'model' => $library,
+                                                 'left(start_at,10)' => $start_at,
+                                                 'order' => 'start_at']));
+    }
+    return $checkouts;
+  }
+
+
   public function findFor($id, $user) {
     return $user
       ? Class_DriveCheckout::findFirstBy(['id' => (int)$id,
@@ -66,7 +80,9 @@ class Class_DriveCheckoutLoader extends Storm_Model_Loader {
 
 
 class Class_DriveCheckout extends Storm_Model_Abstract {
-  use Trait_Translator;
+  use Trait_Translator, Trait_TimeSource;
+
+  const DATETIME_FORMAT = 'Y-m-d H:i:s';
 
   protected
     $_loader_class = 'Class_DriveCheckoutLoader',
@@ -80,6 +96,12 @@ class Class_DriveCheckout extends Storm_Model_Abstract {
     $_date_time_label;
 
 
+  public function beforeSave() {
+    if ($this->isNew())
+      $this->setCreatedAt($this->getCurrentDateTime());
+  }
+
+
   public function getLibraryLabel() {
     return ($library = $this->getLibrary())
       ? $library->getLibelle()
@@ -87,6 +109,11 @@ class Class_DriveCheckout extends Storm_Model_Abstract {
   }
 
 
+  public function getDayLabel() {
+    return strftime('%A %d %B %Y', strtotime($this->getStartAt()));
+  }
+
+
   public function getDateTimeLabel() {
     if (!$this->hasStartAt())
       return $this->_('inconnu');
@@ -104,8 +131,29 @@ class Class_DriveCheckout extends Storm_Model_Abstract {
   }
 
 
+  public function getCreatedAtLabel() {
+    return ($created = $this->getDateTimeCreatedAt())
+      ? strftime($this->_('%d %B à %Hh%M'), $created->getTimestamp())
+      : $this->_('n/a');
+  }
+
+
+  public function getCreatedAtForCsv() {
+    return ($created = $this->getDateTimeCreatedAt())
+      ? $created->format(static::DATETIME_FORMAT)
+      : '';
+  }
+
+
+  public function getDateTimeCreatedAt() {
+    return ($date_time = $this->getCreatedAt()) && $date_time != '0000-00-00 00:00:00'
+      ? DateTime::createFromFormat(static::DATETIME_FORMAT, $date_time)
+      : null;
+  }
+
+
   public function getDateTimeStartAt() {
-    return DateTime::createFromFormat('Y-m-d H:i:s', $this->getStartAt());
+    return DateTime::createFromFormat(static::DATETIME_FORMAT, $this->getStartAt());
   }
 
 
@@ -165,70 +213,11 @@ class Class_DriveCheckout extends Storm_Model_Abstract {
 
 
   public function notify() {
-    return (new Class_DriveCheckout_Notification($this))->send();
-  }
-}
-
-
-
-
-class Class_DriveCheckout_Hold {
-  protected
-    $_checkout,
-    $_hold,
-    $_item;
-
-  public function __construct($checkout, $hold) {
-    $this->_checkout = $checkout;
-    $this->_hold = $hold;
-    $this->_item = $hold->getExemplaireOPAC($checkout->getUser());
-  }
-
-
-  public function getStartAt() {
-    return $this->_checkout->getStartAt();
-  }
-
-
-  public function getIdAbon() {
-    return $this->_checkout->getIdAbon();
+    return (new Class_DriveCheckout_CreationNotification($this))->send();
   }
 
 
-  public function getNomComplet() {
-    return $this->_checkout->getNomComplet();
+  public function notifyDelete() {
+    return (new Class_DriveCheckout_DeletionNotification($this))->send();
   }
-
-
-  public function getCote() {
-    return $this->_item
-      ? $this->_item->getCote()
-      : $this->_hold->getCote();
-  }
-
-
-  public function getStatus() {
-    return $this->_hold->getEtat();
-  }
-
-
-  public function getCodeBarres() {
-    return $this->_item
-      ? $this->_item->getCodeBarres()
-      : $this->_hold->getCodeBarre();
-  }
-
-
-  public function getTitle() {
-    return $this->_item
-      ? $this->_item->getTitrePrincipal()
-      : $this->_hold->getTitre();
-  }
-
-
-  public function getRecord() {
-    return $this->_item
-      ? $this->_item->getNotice()
-      : null;
-  }
-}
\ No newline at end of file
+}
diff --git a/library/Class/DriveCheckout/CreationNotification.php b/library/Class/DriveCheckout/CreationNotification.php
new file mode 100644
index 0000000000000000000000000000000000000000..32adb45d77f71313e48778ccbaa06a52f41cf813
--- /dev/null
+++ b/library/Class/DriveCheckout/CreationNotification.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+class Class_DriveCheckout_CreationNotification extends Class_DriveCheckout_Notification {
+  protected function _contentTemplate() {
+    return Class_AdminVar::getValueOrDefault('DRIVE_TEMPLATE_NEW_RDV_CONTENT');
+  }
+
+
+  protected function _subjectTemplate() {
+    return Class_AdminVar::getValueOrDefault('DRIVE_TEMPLATE_NEW_RDV_SUBJECT');
+  }
+
+
+  protected function _ical() {
+    return (new Class_ICal_DriveCheckout($this->_checkout))
+      ->renderCalendar(Class_Profil::getCurrentProfil());
+  }
+}
diff --git a/library/Class/DriveCheckout/DeletionNotification.php b/library/Class/DriveCheckout/DeletionNotification.php
new file mode 100644
index 0000000000000000000000000000000000000000..585cb4c065c9e3080803b0ee6a7bd0c2bf85ee9d
--- /dev/null
+++ b/library/Class/DriveCheckout/DeletionNotification.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+class Class_DriveCheckout_DeletionNotification extends Class_DriveCheckout_Notification {
+  protected function _contentTemplate() {
+    return Class_AdminVar::getValueOrDefault('DRIVE_TEMPLATE_DELETE_RDV_CONTENT');
+  }
+
+
+  protected function _subjectTemplate() {
+    return Class_AdminVar::getValueOrDefault('DRIVE_TEMPLATE_DELETE_RDV_SUBJECT');
+  }
+
+
+  protected function _ical() {
+    return (new Class_ICal_DriveCheckoutCancel($this->_checkout))
+      ->renderCalendar(Class_Profil::getCurrentProfil());
+  }
+}
diff --git a/library/Class/DriveCheckout/Hold.php b/library/Class/DriveCheckout/Hold.php
new file mode 100644
index 0000000000000000000000000000000000000000..631ca5b63d03046d045ae664ad511dfe94620fed
--- /dev/null
+++ b/library/Class/DriveCheckout/Hold.php
@@ -0,0 +1,109 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Class_DriveCheckout_Hold {
+  use Trait_GetterByAttributeName;
+
+  protected
+    $_checkout,
+    $_hold,
+    $_item;
+
+  public function __construct($checkout, $hold) {
+    $this->_checkout = $checkout;
+    $this->_hold = $hold;
+    $this->_item = $hold->getExemplaireOPAC($checkout->getUser());
+  }
+
+
+  public function getStartAt() {
+    return $this->_checkout->getStartAt();
+  }
+
+
+  public function getIdAbon() {
+    return $this->_checkout->getIdAbon();
+  }
+
+
+  public function getNomComplet() {
+    return $this->_checkout->getNomComplet();
+  }
+
+
+  public function getCreatedAtForCsv() {
+    return $this->_checkout->getCreatedAtForCsv();
+  }
+
+
+  public function getCote() {
+    return $this->_item
+      ? $this->_item->getCote()
+      : $this->_hold->getCote();
+  }
+
+
+  public function getStatus() {
+    return $this->_hold->getEtat();
+  }
+
+
+  public function getCodeBarres() {
+    return $this->_item
+      ? $this->_item->getCodeBarres()
+      : $this->_hold->getCodeBarre();
+  }
+
+
+  public function getTitle() {
+    return $this->_item
+      ? $this->_item->getTitrePrincipal()
+      : $this->_hold->getTitre();
+  }
+
+
+  public function getSectionLabel() {
+    if (!$this->_item || !$section_id = $this->_item->getSection())
+      return '';
+
+    return ($section = Class_CodifSection::find($section_id))
+      ? $section->getLibelle()
+      : '';
+  }
+
+
+  public function getEmplacementLabel() {
+    if (!$this->_item || !$emplacement_id = $this->_item->getEmplacement())
+      return '';
+
+    return ($emplacement = Class_CodifEmplacement::find($emplacement_id))
+      ? $emplacement->getLibelle()
+      : '';
+  }
+
+
+  public function getRecord() {
+    return $this->_item
+      ? $this->_item->getNotice()
+      : null;
+  }
+}
\ No newline at end of file
diff --git a/library/Class/DriveCheckout/Notification.php b/library/Class/DriveCheckout/Notification.php
index dfa2a0eadda60c17f3a39dca31305246b4876a33..393a861996e28b72f7ded7129d4dd783cc83cae3 100644
--- a/library/Class/DriveCheckout/Notification.php
+++ b/library/Class/DriveCheckout/Notification.php
@@ -19,7 +19,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
-class Class_DriveCheckout_Notification {
+abstract class Class_DriveCheckout_Notification {
   protected $_checkout;
 
   public function __construct($checkout) {
@@ -62,22 +62,39 @@ class Class_DriveCheckout_Notification {
                     'user' => $user,
                     'library' => $library];
 
-    $body = (new Class_ModeleFusion())
-      ->setContenu(Class_AdminVar::getValueOrDefault('DRIVE_TEMPLATE_NEW_RDV_CONTENT'))
-      ->setDataSource($data_source)
-      ->getContenuFusionne();
+    $body = $this->_transform($this->_contentTemplate(), $data_source);
+    $subject = $this->_transform($this->_subjectTemplate(), $data_source);
 
-    $subject = (new Class_ModeleFusion())
-      ->setContenu(Class_AdminVar::getValueOrDefault('DRIVE_TEMPLATE_NEW_RDV_SUBJECT'))
-      ->setDataSource($data_source)
-      ->getContenuFusionne();
+    if ($library->hasMail())
+      $mailer->setFrom($library->getMail());
 
     $mail = $mailer->prepare($recipient, $subject, $body, true);
-    $attachment = $mail
-      ->createAttachment((new Class_ICal_DriveCheckout($this->_checkout))->renderCalendar(Class_Profil::getCurrentProfil()),
-                         Class_ICal_DriveCheckout::MIME_TYPE);
-    $attachment->filename = 'calendar.ics';
+
+    if ($ical = $this->_ical()) {
+      $attachment = $mail->createAttachment($ical, Class_ICal_DriveCheckout::MIME_TYPE);
+      $attachment->filename = 'calendar.ics';
+    }
 
     $mailer->send($mail);
   }
+
+
+  protected function _transform($template, $datas) {
+    return (new Class_ModeleFusion())
+      ->setContenu($template)
+      ->setDataSource($datas)
+      ->getContenuFusionne();
+  }
+
+
+  /** @return string Class_ModeleFusion compatible text */
+  abstract protected function _subjectTemplate();
+
+
+  /** @return string Class_ModeleFusion compatible text */
+  abstract protected function _contentTemplate();
+
+
+  /** @return string checkout as an ICAL event */
+  abstract protected function _ical();
 }
diff --git a/library/Class/DriveCheckout/Plan.php b/library/Class/DriveCheckout/Plan.php
index d95755fe2c719bc1f515921925675d05b2eca45d..11f8d4eb95c6bf2e72e7edc19698f4d68130516d 100644
--- a/library/Class/DriveCheckout/Plan.php
+++ b/library/Class/DriveCheckout/Plan.php
@@ -44,6 +44,7 @@ class Class_DriveCheckout_Plan {
   public function __construct($params, $user) {
     $this->_params = $params;
     $this->_user = $user;
+    $this->_should_filter_libraries = Class_AdminVar::isDriveCheckoutShouldFilterLibraries();
   }
 
 
@@ -79,6 +80,16 @@ class Class_DriveCheckout_Plan {
   }
 
 
+  public function onlyPossibleLibrary() {
+    return (!$this->selectedLibrary()
+            && 1 == $this->_getLibraries()->count()
+            && ($library = $this->_getLibraries()->first())
+            && !$this->findFutureFor($library))
+      ? $library
+      : null;
+  }
+
+
   public function injectIntoOpenings($value, $closure) {
     return $this->_getOpenings()->injectInto($value, $closure);
   }
diff --git a/library/Class/DriveCheckout/PlanMessages.php b/library/Class/DriveCheckout/PlanMessages.php
new file mode 100644
index 0000000000000000000000000000000000000000..d697015090e669bb77116b4bc5faccf961909c0f
--- /dev/null
+++ b/library/Class/DriveCheckout/PlanMessages.php
@@ -0,0 +1,143 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Class_DriveCheckout_PlanMessages {
+  use Trait_Translator, Trait_Singleton;
+
+  public function mainTitle() {
+    return $this->_('Planifier le retrait de mes documents');
+  }
+
+
+  public function library() {
+    return $this->_('Site de retrait');
+  }
+
+
+  public function chooseLibrary() {
+    return $this->_('Choisir un site de retrait');
+  }
+
+
+  public function nothingToCheckout() {
+    return $this->_('Aucun document à retirer pour l\'instant');
+  }
+
+
+  public function book() {
+    return $this->_('Prendre rendez-vous');
+  }
+
+
+  public function bookAt($library) {
+    return $this->_('Prendre rendez-vous pour retirer les documents de %s',
+                    $library->getLibelle());
+  }
+
+
+  public function chooseAnotherLibrary() {
+    return $this->_('Choisir un autre site');
+  }
+
+
+  public function backToLibraryChoice() {
+    return $this->_('Retourner à la liste des sites de retrait');
+  }
+
+
+  public function checkoutBookedAt($existing) {
+    return $this->_('Retrait planifié %s', $existing->getDateTimeLabel());
+  }
+
+
+  public function addToAgenda() {
+    return $this->_('Ajouter à mon agenda');
+  }
+
+
+  public function importFromIcal($library, $existing) {
+    return $this->_('Importer le retrait de %s pour %s au format ICal',
+                    $library->getLibelle(),
+                    $existing->getDateTimeLabel());
+  }
+
+
+  public function delete() {
+    return $this->_('Annuler ou replanifier');
+  }
+
+
+  public function deleteExistingAt($library, $existing) {
+    return $this->_('Annuler ou replanifier le retrait de %s pour %s',
+                    $library->getLibelle(),
+                    $existing->getDateTimeLabel());
+  }
+
+
+  public function deleteConfirm() {
+    return $this->_('Veuillez confirmer l\'annulation du rendez-vous courant.\nVous pourrez replanifier un rendez-vous au plus tôt à partir de demain.');
+  }
+
+
+  public function holdCount($count) {
+    return $this->_plural($count,
+                          'Aucune réservation',
+                          '%s réservation',
+                          '%s réservations',
+                          $count);
+  }
+
+
+  public function readyCount($count) {
+    return $this->_('%s à retirer', $count);
+  }
+
+
+  public function date() {
+    return $this->_('Date de retrait');
+  }
+
+
+  public function chooseDate($label) {
+    return $this->_('Choisir de retirer les documents %s', $label);
+  }
+
+
+  public function chooseAnotherDate() {
+    return $this->_('Choisir une autre date');
+  }
+
+
+  public function backToDateChoiceOf($library) {
+    return $this->_('Retourner à la liste des dates de retrait pour %s', $library->getLibelle());
+  }
+
+
+  public function time() {
+    return $this->_('Heure de retrait');
+  }
+
+
+  public function chooseTime() {
+    return $this->_('Retirer les documents à :');
+  }
+}
diff --git a/library/Class/Entity.php b/library/Class/Entity.php
index 10bab2a777e5dcc2c37cedf96e01097e18898833..998bf5f104304f616db80906c53fc08c2f83fe36 100644
--- a/library/Class/Entity.php
+++ b/library/Class/Entity.php
@@ -21,6 +21,8 @@
 
 
 class Class_Entity {
+  use Trait_GetterByAttributeName;
+
   protected
     $_attribs = [],
     $_methods = [];
@@ -80,11 +82,6 @@ class Class_Entity {
   }
 
 
-  public function callGetterByAttributeName($attribute) {
-    return call_user_func([$this, 'get' . Storm_Inflector::camelize($attribute)]);
-  }
-
-
   public function __toString() {
     return get_class($this);
   }
diff --git a/library/Class/ICal/DriveCheckout.php b/library/Class/ICal/DriveCheckout.php
index c859a861b1ea568c2de9d612cd4b1a324efc44be..1b58c91dc5638a2e2e367afffbf0b29d1c057270 100644
--- a/library/Class/ICal/DriveCheckout.php
+++ b/library/Class/ICal/DriveCheckout.php
@@ -29,31 +29,37 @@ class Class_ICal_DriveCheckout extends Class_ICal_Abstract {
 
     Class_ICal_Autoloader::getInstance()->ensureAutoload();
 
-    $event = $this->_newEvent();
-    $event->setUseTimezone(true);
-    $event->setDtStart($this->_model->getDateTimeStartAt());
-    $event->setSummary($this->_('Retrait des réservations à %s', $this->_model->getLibraryLabel()));
-    $event->setLocation($this->_model->getLibraryLabel());
-    if (($library = $this->_model->getLibrary())
-        && ($latitude = $library->getLatitude())
-        && ($longitude = $library->getLongitude()))
-      $event->setGeoLocation(new \Eluceo\iCal\Property\Event\Geo($latitude, $longitude));
+    $calendar = $this->_calendar();
+    $calendar->addComponent($this->_event($profil));
 
-    $event->setUrl(Class_Url::absolute(['controller' => 'drive-checkout',
-                                         'action' => 'plan',
-                                         'id_profil' => $profil->getId()],
-                                        null, true));
+    return $calendar->render();
+  }
 
-    $calendar = new \Eluceo\iCal\Component\Calendar(static::PRODUCT_ID);
-    $calendar->addComponent($event);
 
-    return $calendar->render();
+  protected function _calendar() {
+    return new \Eluceo\iCal\Component\Calendar(static::PRODUCT_ID);
   }
 
 
-  protected function _newEvent() {
-    return new \Eluceo\iCal\Component\Event(Class_Url::absolute([], null, true)
-                                            . static::PREFIX_ID
-                                            . $this->_model->getId());
+  protected function _event($profil) {
+    $event = (new \Eluceo\iCal\Component\Event(Class_Url::absolute([], null, true)
+                                               . static::PREFIX_ID
+                                               . $this->_model->getId()))
+      ->setUseTimezone(true)
+      ->setDtStart($this->_model->getDateTimeStartAt())
+      ->setSummary($this->_('Retrait des réservations à %s',
+                            $this->_model->getLibraryLabel()))
+      ->setLocation($this->_model->getLibraryLabel())
+      ->setUrl(Class_Url::absolute(['controller' => 'drive-checkout',
+                                    'action' => 'plan',
+                                    'id_profil' => $profil->getId()],
+                                   null, true));
+
+    if (($library = $this->_model->getLibrary())
+        && ($latitude = $library->getLatitude())
+        && ($longitude = $library->getLongitude()))
+      $event->setGeoLocation(new \Eluceo\iCal\Property\Event\Geo($latitude, $longitude));
+
+    return $event;
   }
 }
diff --git a/library/Class/ICal/DriveCheckoutCancel.php b/library/Class/ICal/DriveCheckoutCancel.php
new file mode 100644
index 0000000000000000000000000000000000000000..13774d159ca8a7e22f2d1bfbc05662afa731aa7d
--- /dev/null
+++ b/library/Class/ICal/DriveCheckoutCancel.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Class_ICal_DriveCheckoutCancel extends Class_ICal_DriveCheckout {
+  protected function _event($profil) {
+    return parent::_event($profil)->setCancelled(true);
+  }
+
+
+  protected function _calendar() {
+    return parent::_calendar()->setMethod(\Eluceo\iCal\Component\Calendar::METHOD_CANCEL);
+  }
+}
diff --git a/library/Class/Mail.php b/library/Class/Mail.php
index 9c0315f465b4ab813b272326707f28c92c636258..9e0f96e632cd06c1f188554146c6415cc1be669c 100644
--- a/library/Class/Mail.php
+++ b/library/Class/Mail.php
@@ -22,20 +22,30 @@
 class Class_Mail {
   use Trait_Translator;
 
-  private $params_ok=false;               // Flag de controle si un mail peut etre envoyé
+  private $params_ok = false;               // Flag de controle si un mail peut etre envoyé
   private $mail_from;                     // Header from
 
   public function __construct() {
     $this->params_ok = false;
 
-    $this->mail_from = Class_Profil::getCurrentProfil()->getMailSiteOrPortail();
-    if (!$this->mail_from)
-      $this->mail_from = Class_CosmoVar::get('mail_admin');
+    if (!$mail_from = Class_Profil::getCurrentProfil()->getMailSiteOrPortail())
+      $mail_from = Class_CosmoVar::get('mail_admin');
+
+    $this->setFrom($mail_from);
+  }
+
+
+  public function setFrom($mail_from) {
+    $this->mail_from = $mail_from;
 
     if ($this->isMailValid($this->mail_from)) {
       ini_set('sendmail_from', $this->mail_from);
-      $this->params_ok=true;
+      $this->params_ok = true;
+      return $this;
     }
+
+    $this->params_ok = false;
+    return $this;
   }
 
 
diff --git a/library/Class/TableDescription/DriveCheckout/Holds.php b/library/Class/TableDescription/DriveCheckout/Holds.php
index 7fcfd881fd621ab9b108aa44f8a3dc3edabaec59..acb1d31f13cc46768347ab4e366b85dec49648ef 100644
--- a/library/Class/TableDescription/DriveCheckout/Holds.php
+++ b/library/Class/TableDescription/DriveCheckout/Holds.php
@@ -23,6 +23,12 @@
 class Class_TableDescription_DriveCheckout_Holds extends Class_TableDescription {
   public function init() {
     $this
+      ->addColumn($this->_('Section'),
+                  function($hold) { return $hold->getSectionLabel(); })
+
+      ->addColumn($this->_('Emplacement'),
+                  function($hold) { return $hold->getEmplacementLabel(); })
+
       ->addColumn($this->_('Cote'),
                   function($hold) { return $hold->getCote(); })
 
diff --git a/library/Class/TableDescription/DriveCheckout/HoldsWithCheckouts.php b/library/Class/TableDescription/DriveCheckout/HoldsWithCheckouts.php
index 62257a5fb78f223fb5f558db8552a5362260eb3e..7a3659267947dcae8de549b9b78f045e366cd142 100644
--- a/library/Class/TableDescription/DriveCheckout/HoldsWithCheckouts.php
+++ b/library/Class/TableDescription/DriveCheckout/HoldsWithCheckouts.php
@@ -27,15 +27,14 @@ class Class_TableDescription_DriveCheckout_HoldsWithCheckouts extends Class_Tabl
                   function($hold) { return strftime('%d %B',strtotime($hold->getStartAt())); })
       ->addColumn($this->_('Heure'),
                   function($hold) { return strftime('%H:%M',strtotime($hold->getStartAt())); })
-      ->addColumn($this->_('Carte'),
-                  function($hold) { return $hold->getIdabon(); })
-      ->addColumn($this->_('Abonné'),
-                  function($hold) { return $hold->getNomComplet(); })
-      ->addColumn($this->_('Cote'),
-                  function($hold) { return $hold->getCote(); })
 
-      ->addColumn($this->_('Code-barres'),
-                  function($hold) { return $hold->getCodeBarres(); })
+      ->addColumn($this->_('Carte'), 'idabon')
+      ->addColumn($this->_('Abonné'), 'nom_complet')
+      ->addColumn($this->_('Créé le'), 'created_at_for_csv')
+      ->addColumn($this->_('Section'), 'section_label')
+      ->addColumn($this->_('Emplacement'), 'emplacement_label')
+      ->addColumn($this->_('Cote'), 'cote')
+      ->addColumn($this->_('Code-barres'), 'code_barres')
 
       ->addColumn($this->_('Titre'),
                   function($hold){ return strip_tags($hold->getTitle()); });
diff --git a/library/Class/TableDescription/DriveCheckout/List.php b/library/Class/TableDescription/DriveCheckout/List.php
index 5caf56025d8585c724e02241bd0adcbbdfb48802..b648ca6153bb8ce28d6a662f334391fad431a8d9 100644
--- a/library/Class/TableDescription/DriveCheckout/List.php
+++ b/library/Class/TableDescription/DriveCheckout/List.php
@@ -23,6 +23,12 @@
 class Class_TableDescription_DriveCheckout_List extends Class_TableDescription {
   public function init() {
     $this
+      ->addColumn($this->_('Jour'),
+                  function($checkout)
+                  {
+                    return strftime('%d %B',
+                                    strtotime($checkout->getStartAt()));
+                  })
       ->addColumn($this->_('Heure'),
                   function($checkout)
                   {
@@ -30,6 +36,8 @@ class Class_TableDescription_DriveCheckout_List extends Class_TableDescription {
                                     strtotime($checkout->getStartAt()));
                   })
       ->addColumn($this->_('Carte'), 'id_abon')
-      ->addColumn($this->_('Abonné'), 'nom_complet');
+      ->addColumn($this->_('Abonné'), 'nom_complet')
+      ->addColumn($this->_('Créé le'), 'created_at_for_csv')
+      ;
   }
 }
\ No newline at end of file
diff --git a/library/Class/TableDescription/DriveCheckout/ListWithActions.php b/library/Class/TableDescription/DriveCheckout/ListWithActions.php
index a1c2971cad301b23750a623f98d85a2a4a1e2226..a56c6efa3d8c1740e3ba0eed6f2524f9f534a62d 100644
--- a/library/Class/TableDescription/DriveCheckout/ListWithActions.php
+++ b/library/Class/TableDescription/DriveCheckout/ListWithActions.php
@@ -20,13 +20,23 @@
  */
 
 
-class Class_TableDescription_DriveCheckout_ListWithActions extends Class_TableDescription_DriveCheckout_List {
+class Class_TableDescription_DriveCheckout_ListWithActions extends Class_TableDescription {
   public function init() {
     parent::init();
     $this
+      ->addColumn($this->_('Heure'),
+                  function($checkout)
+                  {
+                    return strftime('%H:%M',
+                                    strtotime($checkout->getStartAt()));
+                  })
+      ->addColumn($this->_('Carte'), 'id_abon')
+      ->addColumn($this->_('Abonné'), 'nom_complet')
+      ->addColumn($this->_('Créé le'), 'created_at_label')
       ->addRowAction(['canvas_callback' => [$this, 'listHolds']])
       ->addRowAction(['canvas_callback' => [$this, 'viewUser']])
-      ->addRowAction(['canvas_callback' => [$this, 'deleteCheckout']]);
+      ->addRowAction(['canvas_callback' => [$this, 'deleteCheckout']])
+      ->setSorterNone();
   }
 
 
diff --git a/library/Class/TableDescription/PNBItemsRenderer.php b/library/Class/TableDescription/PNBItemsRenderer.php
index f40234de32f08f3a93b4602894100cdbacbbaf3b..72c48ce68b45d58c11bd03a7a3a96a78a6d7bef8 100644
--- a/library/Class/TableDescription/PNBItemsRenderer.php
+++ b/library/Class/TableDescription/PNBItemsRenderer.php
@@ -21,6 +21,8 @@
 
 
 class Class_TableDescription_PNBItemsRenderer {
+  use Trait_GetterByAttributeName;
+
   protected $_item;
 
   public function __construct($item) {
@@ -28,11 +30,6 @@ class Class_TableDescription_PNBItemsRenderer {
   }
 
 
-  public function callGetterByAttributeName($attribute) {
-    return call_user_func([$this, 'get' . Storm_Inflector::camelize($attribute)]);
-  }
-
-
   public function __call($name, $params) {
     return call_user_func_array([$this->_item, $name], $params);
   }
diff --git a/library/Class/User/Cards.php b/library/Class/User/Cards.php
index bc7207a3799146d9a5e4c3a8ade0da6d60e23779..fd9c4cc1fd82f2473884ec7672dccc520a70d2db 100644
--- a/library/Class/User/Cards.php
+++ b/library/Class/User/Cards.php
@@ -208,13 +208,7 @@ class Class_User_Cards extends Storm_Model_Collection {
                                            'Vous avez %d documents en retard.',
                                            $late_loans_count));
 
-      $actions = Class_AdminVar::isModuleEnabled('ENABLE_DRIVE_CHECKOUT')
-        ? ['actions' =>
-           [
-            Class_Url::assemble(['module' => 'opac',
-                                 'controller' => 'drive-checkout',
-                                 'action' => 'plan']) => $this->_('Planifier le retrait de mes documents')]]
-        : [];
+      $actions = $this->_driveNotificationActions();
 
 
       if ($pullable_items_count = $this->countWaitingToBePulled()) {
@@ -232,4 +226,24 @@ class Class_User_Cards extends Storm_Model_Collection {
 
     return $this;
   }
+
+
+  protected function _driveNotificationActions() {
+    if (!Class_AdminVar::isModuleEnabled('ENABLE_DRIVE_CHECKOUT'))
+      return [];
+
+    $has_library = $this->detect(function($card)
+                                 {
+                                   return (new Class_DriveCheckout_Plan([], $card))->hasLibraries();
+                                 });
+
+    if (!$has_library)
+      return [];
+
+    return ['actions' =>
+            [
+             Class_Url::assemble(['module' => 'opac',
+                                  'controller' => 'drive-checkout',
+                                  'action' => 'plan']) => $this->_('Planifier le retrait de mes documents')]];
+  }
 }
\ No newline at end of file
diff --git a/library/Class/Users.php b/library/Class/Users.php
index 83a9052155fdd60bb45db67a26f9019083c88646..61840f66778b43d753171c73c71337e37b613612 100644
--- a/library/Class/Users.php
+++ b/library/Class/Users.php
@@ -1333,6 +1333,11 @@ class Class_Users extends Storm_Model_Abstract {
   }
 
 
+  public function getHoldsWaitingToBePulled() {
+    return $this->getEmprunteur()->getHoldsWaitingToBePulled();
+  }
+
+
   public function getFicheSigb($user = null) {
     if ($user === null)
       $user = $this; // compatibilité
diff --git a/library/Trait/GetterByAttributeName.php b/library/Trait/GetterByAttributeName.php
new file mode 100644
index 0000000000000000000000000000000000000000..4b3208ef0a2cafb3635980e5d6f1e9363dd3cb6a
--- /dev/null
+++ b/library/Trait/GetterByAttributeName.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+trait Trait_GetterByAttributeName {
+  public function callGetterByAttributeName($name) {
+    return call_user_func([$this, 'get' . Storm_Inflector::camelize($name)]);
+  }
+}
diff --git a/library/ZendAfi/View/Helper/DriveCheckoutPlan.php b/library/ZendAfi/View/Helper/DriveCheckoutPlan.php
index e531fe1e209fb5dd43ca55d15417bd89ef5ffefe..1df24949c5a2bec3107d5107ecaca9d984f2fee0 100644
--- a/library/ZendAfi/View/Helper/DriveCheckoutPlan.php
+++ b/library/ZendAfi/View/Helper/DriveCheckoutPlan.php
@@ -23,7 +23,8 @@
 class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelper {
   protected
     $_plan,
-    $_user;
+    $_user,
+    $_messages;
 
   /**
    * @param $plan Class_DriveCheckout_Plan
@@ -34,10 +35,11 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp
       return '';
 
     $this->_plan = $plan;
+    $this->_messages = Class_DriveCheckout_PlanMessages::getInstance();
 
     return
-      $this->view->openBoiteContent($this->_('Planifier le retrait de mes documents'))
-      . $this->_tag('h2', $this->_('Site de retrait'))
+      $this->view->openBoiteContent($this->_messages->mainTitle())
+      . $this->_tag('h2', $this->_messages->library())
       . $this->_libraries()
       . $this->_dates()
       . $this->_times()
@@ -50,7 +52,7 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp
       return $this->_selectedLibrary($library);
 
     if (!$this->_plan->hasLibraries())
-      return $this->_('Aucun document à retirer pour l\'instant');
+      return $this->_messages->nothingToCheckout();
 
     $list = $this->_plan
       ->injectIntoLibraries([],
@@ -69,8 +71,8 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp
       ->_tag('h3',
              $library->getLibelle()
              . ' ' . $this->_tagAnchor($this->_plan->resetUrl(),
-                                       $this->_('modifier'),
-                                       ['title' => $this->_('retour à la liste des sites de retrait')])
+                                       $this->_messages->chooseAnotherLibrary(),
+                                       ['title' => $this->_messages->backToLibraryChoice()])
              . BR . $this->_holdsInfoFor($library));
   }
 
@@ -80,32 +82,29 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp
       return $this
         ->_tag('li',
                $library->getLibelle()
-               . BR . $this->_('Retrait planifié %s', $existing->getDateTimeLabel())
+               . BR . $this->_messages->checkoutBookedAt($existing)
                . ' '
                . $this->_tagAnchor(['action' => 'ical',
                                     'id' => $existing->getId()],
-                                   $this->_('Ajouter à mon agenda'),
-                                   ['title' => $this->_('Importer le retrait de %s pour %s au format ICal',
-                                                        $library->getLibelle(),
-                                                        $existing->getDateTimeLabel()),
-                                    ])
+                                   $this->_messages->addToAgenda(),
+                                   ['title' => $this->_messages->importFromIcal($library, $existing)])
                . ' / '
                . $this->_tagAnchor(['action' => 'delete',
                                     'id' => $existing->getId()],
-                                   $this->_('Supprimer'),
-                                   ['title' => $this->_('Supprimer le retrait de %s pour %s',
-                                                        $library->getLibelle(),
-                                                        $existing->getDateTimeLabel()),
-                                    'onclick' => 'return confirm(\''.  $this->_escapeJsAttrib($this->_('Etes vous sûr de vouloir supprimer ce retrait ?')) . '\')'])
+                                   $this->_messages->delete(),
+                                   ['title' => $this->_messages->deleteExistingAt($library,
+                                                                                  $existing),
+                                    'onclick' => 'return confirm(\''.  $this->_escapeJsAttrib($this->_messages->deleteConfirm()) . '\')'])
                . BR . $this->_holdsInfoFor($library));
     }
 
     return $this
       ->_tag('li',
-             $this->_tagAnchor($this->_plan->libraryUrlFor($library),
-                               $library->getLibelle(),
-                               ['title' => $this->_('Choisir de retirer les documents de %s',
-                                                    $library->getLibelle())])
+             $library->getLibelle()
+             . ' '
+             . $this->_tagAnchor($this->_plan->libraryUrlFor($library),
+                                 $this->_messages->book(),
+                                 ['title' => $this->_messages->bookAt($library)])
              . BR . $this->_holdsInfoFor($library));
   }
 
@@ -114,16 +113,10 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp
     $holds = $this->_plan->holds()->selectLibrary($library);
     $total = $holds->count();
     $ready = $holds->countWaitingToBePulled();
-    $not_ready = $total - $ready;
-
-    return sprintf('%s, %s, %s',
-                   $this->_plural($total,
-                                  'Aucune réservation',
-                                  '%s réservation',
-                                  '%s réservations',
-                                  $total),
-                   $this->_('%s à retirer', $ready),
-                   $this->_('%s en attente de traitement', $not_ready));
+
+    return sprintf('%s, %s',
+                   $this->_messages->holdCount($total),
+                   $this->_messages->readyCount($ready));
   }
 
 
@@ -142,7 +135,7 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp
                              return $value;
                            });
 
-    return $this->_tag('h2', $this->_('Date de retrait'))
+    return $this->_tag('h2', $this->_messages->date())
       . $this->_tag('ul', implode($list));
   }
 
@@ -154,8 +147,7 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp
       ->_tag('li',
              $this->_tagAnchor($this->_plan->dateUrlFor($opening),
                                $label,
-                               ['title' => $this->_('Choisir de retirer les documets %s',
-                                                    $label)]));
+                               ['title' => $this->_messages->chooseDate($label)]));
   }
 
 
@@ -163,12 +155,12 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp
     $library = $this->_plan->selectedLibrary();
 
     return
-      $this->_tag('h2', $this->_('Date de retrait'))
+      $this->_tag('h2', $this->_messages->date())
       . $this->_tag('h3',
                     $this->_dateLabel($date)
                     . ' ' . $this->_tagAnchor($this->_plan->libraryUrlFor($library),
-                                              $this->_('modifier'),
-                                              ['title' => $this->_('retour à la liste des dates de retrait pour %s', $library->getLibelle())]));
+                                              $this->_messages->chooseAnotherDate(),
+                                              ['title' => $this->_messages->backToDateChoiceOf($library)]));
   }
 
 
@@ -184,12 +176,12 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp
 
     $form = (new ZendAfi_Form(['data-backurl' => $this->view->url($this->_plan->dateUrlFor($selected_date))]))
       ->addElement('select', 'checkout_time',
-                   ['label' => $this->_('Retirer les documents à :'),
+                   ['label' => $this->_messages->chooseTime(),
                     'multiOptions' => $this->_plan->timesAsMultioptions()])
       ->addUniqDisplayGroup('default')
       ;
 
-    return $this->_tag('h2', $this->_('Heure de retrait'))
+    return $this->_tag('h2', $this->_messages->time())
       . $this->view->renderForm($form);
   }
 }
diff --git a/library/ZendAfi/View/Helper/RenderCsv.php b/library/ZendAfi/View/Helper/RenderCsv.php
index 787e20fd588326c2aba506a34eb030ab80c82052..233ea3fdfbb5a2e0247937469e44c6b8bb4e7481 100644
--- a/library/ZendAfi/View/Helper/RenderCsv.php
+++ b/library/ZendAfi/View/Helper/RenderCsv.php
@@ -34,7 +34,7 @@ class ZendAfi_View_Helper_RenderCsv extends ZendAfi_View_Helper_BaseHelper {
     $this->_fp_csv = fopen('php://memory', 'r+');
 
     $this->_renderHeaders();
-    $this->_renderDatas($models);
+    $this->_renderDatas((array)$models);
 
     rewind($this->_fp_csv);
     return stream_get_contents($this->_fp_csv);
diff --git a/library/ZendAfi/View/Helper/RenderTable.php b/library/ZendAfi/View/Helper/RenderTable.php
index 932d377154330b214c4be02c3fffc81d6e5eb5f9..bc72812ff7b4209a7325c619d3117829b8bde4b5 100644
--- a/library/ZendAfi/View/Helper/RenderTable.php
+++ b/library/ZendAfi/View/Helper/RenderTable.php
@@ -177,8 +177,8 @@ class ZendAfi_View_Helper_RenderTable_Body extends ZendAfi_View_Helper_BaseHelpe
      $this->_html .= $this->_tag('tr',
                                 $this->_tag('td',
                                             $this->view->escape($title),
-                                            ['style' => 'background-color:#888;color:white;font-size:120%;padding:2px 10px;font-weight:bold;',
-                                             'colspan' => $this->_description->numberOfColumns()]));
+                                            ['colspan' => $this->_description->numberOfColumns()]),
+                                 ['class' => 'group_title']);
   }
 
 
diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan.php
index 1340b09a17ae29d5003235d0736bcb1ef415c81f..86a2453bc777de78063da5885473677f729fd2cd 100644
--- a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan.php
+++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan.php
@@ -24,7 +24,7 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan
   extends Intonation_Library_View_Wrapper_Abstract {
 
   public function getMainTitle() {
-    return $this->_('Planifier le retrait de mes documents');
+    return Class_DriveCheckout_PlanMessages::getInstance()->mainTitle();
   }
 
 
diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/Library.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/Library.php
index 570e53bbd95135ca16a18b0bdc6537b6ef1b63c5..5e024bb9c2f7513083b30c98194572a79e464fcf 100644
--- a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/Library.php
+++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/Library.php
@@ -26,10 +26,12 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library
   protected
     $_plan,
     $_html_id,
-    $_existing;
+    $_existing,
+    $_messages;
 
   public function setPlan($plan) {
     $this->_plan = $plan;
+    $this->_messages = Class_DriveCheckout_PlanMessages::getInstance();
     return $this;
   }
 
@@ -62,9 +64,8 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library
                                                                                  'available',
                                                                                  'library'),
 
-                                    'Text' => $this->_('Prendre rendez-vous'),
-                                    'Title' => $this->_('Prendre rendez-vous pour retirer les documents de %s',
-                                                        $this->_model->getLibelle()),
+                                    'Text' => $this->_messages->book(),
+                                    'Title' => $this->_messages->bookAt($this->_model),
                                     'Class' => 'text-primary'])];
   }
 
@@ -77,10 +78,9 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library
                                                                                  'extend-loan',
                                                                                  'library'),
 
-                                    'Text' => $this->_('Ajouter à mon agenda'),
-                                    'Title' => $this->_('Importer le retrait de %s pour %s au format ICal',
-                                                        $this->_model->getLibelle(),
-                                                        $existing->getDateTimeLabel()),
+                                    'Text' => $this->_messages->addToAgenda(),
+                                    'Title' => $this->_messages->importFromIcal($this->_model,
+                                                                                $existing),
                                     ]),
        new Intonation_Library_Link(['Url' => $this->_view->url(['action' => 'delete',
                                                                 'id' => $existing->getId()]),
@@ -88,12 +88,11 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library
                                                                                  'clean',
                                                                                  'utils'),
 
-                                    'Text' => $this->_('Supprimer'),
-                                    'Title' => $this->_('Supprimer le retrait de %s pour %s',
-                                                        $this->_model->getLibelle(),
-                                                        $existing->getDateTimeLabel()),
+                                    'Text' => $this->_messages->delete(),
+                                    'Title' => $this->_messages->deleteExistingAt($this->_model,
+                                                                                  $existing),
                                     'Class' => 'text-danger',
-                                    'Attribs' => ['onclick' => $this->_view->confirm($this->_('Etes vous sûr de vouloir supprimer ce retrait ?'))]])];
+                                    'Attribs' => ['onclick' => $this->_view->confirm($this->_messages->deleteConfirm())]])];
   }
 
 
@@ -102,7 +101,7 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library
       ? $this->_view->tag('p',
                           Class_Template::current()->getIco($this->_view, 'available', 'library')
                           . ' '
-                          . $this->_('Retrait planifié %s', $existing->getDateTimeLabel()),
+                          . $this->_messages->checkoutBookedAt($existing),
                           ['class' => 'text-success'])
       : '';
 
@@ -127,33 +126,17 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library
             ((new Intonation_Library_Badge)
              ->setTag('span')
              ->setClass('primary')
-             ->setText($this->_plural($total,
-                                      'Aucune réservation',
-                                      '%s réservation',
-                                      '%s réservations',
-                                      $total))
+             ->setText($this->_messages->holdCount($total))
              ->setImage(Class_Template::current()->getIco($this->_view,
                                                           'loan',
-                                                          'library'))
-             ->setTitle($this->_plural($total,
-                                       'Aucun document réservé',
-                                       '%s document réservé',
-                                       '%s documents réservés',
-                                       $total))),
-
+                                                          'library'))),
             ((new Intonation_Library_Badge)
              ->setTag('span')
              ->setClass('success')
-             ->setText($this->_('%s à retirer', $ready))
+             ->setText($this->_messages->readyCount($ready))
              ->setImage(Class_Template::current()->getIco($this->_view,
                                                           'loan',
-                                                          'library'))
-             ->setTitle($this->_plural($ready,
-                                       'Aucun document ne peut être retiré',
-                                       '%s document peut être retiré',
-                                       '%s documents peuvent être retirés',
-                                       $ready))),
-    ];
+                                                          'library')))];
   }
 
 
diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/LibrarySelected.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/LibrarySelected.php
index 24477757c9befdc9e7194ed4eda287a66d1e9790..13aa549ed37475753d2e08a8566bea5db0abfd8c 100644
--- a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/LibrarySelected.php
+++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/LibrarySelected.php
@@ -32,8 +32,8 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_LibrarySelected
                                                                                       'refresh',
                                                                                       'utils'),
 
-                                         'Text' => $this->_('Choisir un autre site'),
-                                         'Title' => $this->_('Retourner à la liste des sites de retrait'),
+                                         'Text' => $this->_messages->chooseAnotherLibrary(),
+                                         'Title' => $this->_messages->backToLibraryChoice(),
                                          'Class' => 'text-danger'])];
   }
 }
diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Date.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Date.php
index 33a686af5c0690d8b3595807c2fe99d6dfb03734..d9a01a8cba6f8d9bb361f4f01e28004053956691 100644
--- a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Date.php
+++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Date.php
@@ -25,13 +25,21 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Date
 
   const HTML_CLASS = 'drivecheckout_plan_date';
 
+  protected $_messages;
+
+  public function setModel($model) {
+    $this->_messages = Class_DriveCheckout_PlanMessages::getInstance();
+    return parent::setModel($model);
+  }
+
+
   public static function htmlIdFor($model) {
     return static::HTML_CLASS . '_' . $model->getId();
   }
 
 
   public function getTitle() {
-    return $this->_('Date de retrait');
+    return $this->_messages->date();
   }
 
 
@@ -80,8 +88,7 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Date
                                                                                         'available',
                                                                                         'library'),
                                            'Text' => $label,
-                                           'Title' => $this->_('Choisir de retirer les documents %s',
-                                                               $label)]);
+                                           'Title' => $this->_messages->chooseDate($label)]);
 
     return $this->_view->tagAction($action);
   }
@@ -93,9 +100,8 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Date
     $url = $this->_view->url($url_parts) . ($url_parts ? '#' . $this->_getHtmlId() : '');
     $attribs = ['Url' => $url,
                 'Image' => Class_Template::current()->getIco($this->_view, 'refresh', 'utils'),
-                'Text' => $this->_('Choisir une autre date'),
-                'Title' => $this->_('Retourner à la liste des dates de retrait pour %s',
-                                    $this->_model->selectedLibrary()->getLibelle()),
+                'Text' => $this->_messages->chooseAnotherDate(),
+                'Title' => $this->_messages->backToDateChoiceOf($this->_model->selectedLibrary()),
                 'Class' => 'text-danger'];
 
     return $label . BR . $this->_view->tagAction(new Intonation_Library_Link($attribs));
diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Library.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Library.php
index 4adcf29ccea332ea4490b6b624fb1618c8c4e6d4..cf4e988d0a457c3d8c7f273b80d272473d952baf 100644
--- a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Library.php
+++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Library.php
@@ -22,9 +22,16 @@
 
 class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Library
   extends Intonation_Library_View_Wrapper_RichContent_Section{
+  protected $_messages;
+
+  public function setModel($model) {
+    $this->_messages = Class_DriveCheckout_PlanMessages::getInstance();
+    return parent::setModel($model);
+  }
+
 
   public function getTitle() {
-    return $this->_('Site de retrait');
+    return $this->_messages->library();
   }
 
 
@@ -36,7 +43,7 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Library
 
     if ($librairies->isEmpty())
       return $this->_content = $this->_view
-        ->tag('p', $this->_('Aucun document à retirer pour l\'instant'));
+        ->tag('p', $this->_messages->nothingToCheckout());
 
     $wrapper_class = $this->_wrapperClass();
 
@@ -94,6 +101,6 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Library
 
 
   public function getNavTitle() {
-    return $this->_('Choisir un site de retrait');
+    return $this->_messages->chooseLibrary();
   }
 }
diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Time.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Time.php
index 0deff0716efcee48aa9f03508e820941ef8280c7..5ca09b2583261760e7b6ecdaa0f19eb1ce318d14 100644
--- a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Time.php
+++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Time.php
@@ -25,6 +25,13 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Time
 
   const HTML_CLASS = 'drivecheckout_plan_time';
 
+  protected $_messages;
+
+  public function setModel($model) {
+    $this->_messages = Class_DriveCheckout_PlanMessages::getInstance();
+    return parent::setModel($model);
+  }
+
 
   public static function htmlIdFor($model) {
     return static::HTML_CLASS . '_' . $model->getId();
@@ -32,7 +39,7 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Time
 
 
   public function getTitle() {
-    return $this->_('Heure de retrait');
+    return $this->_messages->time();
   }
 
 
@@ -45,7 +52,7 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Time
 
     $form = (new ZendAfi_Form(['data-backurl' => $this->_view->url($this->getNavUrl())]))
       ->addElement('select', 'checkout_time',
-                   ['label' => $this->_('Retirer les documents à :'),
+                   ['label' => $this->_messages->chooseTime(),
                     'multiOptions' => $this->_model->timesAsMultioptions()])
       ->addUniqDisplayGroup('default')
       ;
diff --git a/public/admin/css/global.css b/public/admin/css/global.css
index aa0c5b1bab3232d095fb7b3fde83c7f81de062dc..7d929b9941087513468b0fda76b05dad8fd8bb3b 100644
--- a/public/admin/css/global.css
+++ b/public/admin/css/global.css
@@ -936,6 +936,16 @@ table.models > tbody > tr:hover {
 }
 
 
+.modules table.models .group_title {
+    background-color: #888;
+    color:white;
+    font-size:120%;
+    padding:2px 10px;
+    font-weight:bold;
+}
+
+
+
 .profils ul li ul.profil_pages li:hover {
     cursor: move;
 }
@@ -1670,4 +1680,4 @@ table#logs img {
     padding: 1ex 1em;
     overflow: auto;
     max-height: 300px;
-}
\ No newline at end of file
+}
diff --git a/public/admin/skins/bokeh74/global.css b/public/admin/skins/bokeh74/global.css
index c9fff349496f943f289e27f9044be2bcaacc0f0c..722335284fa278e4081b0a833cc8d6af198db6f9 100755
--- a/public/admin/skins/bokeh74/global.css
+++ b/public/admin/skins/bokeh74/global.css
@@ -371,6 +371,14 @@ table#suggestions td:last-child a {
     min-width: 100px;
 }
 
+.modules table.models .group_title {
+    background-color:var(--nav-background);
+    color:white;
+    font-size:120%;
+    padding:2px 10px;
+    font-weight:bold;
+}
+
 .treeView .ui-accordion .ui-accordion-content {
     padding-top: 0.5em;
 }
@@ -1055,4 +1063,4 @@ table#logs img {
 #learn_more button:focus, 
 #learn_more button:hover {
   background-color:var(--bokeh-event-highlight);
-}
\ No newline at end of file
+}
diff --git a/public/admin/skins/noel/global.css b/public/admin/skins/noel/global.css
index 51dd45fe6c9166aeec285c7fd378f8fc14275d4c..1f34ae375ca2121fae025f5de2e9cdd3d0469277 100755
--- a/public/admin/skins/noel/global.css
+++ b/public/admin/skins/noel/global.css
@@ -391,6 +391,14 @@ table#suggestions td:last-child a {
     min-width: 100px;
 }
 
+
+.modules table.models .group_title {
+    color:white;
+    font-size:120%;
+    padding:2px 10px;
+    font-weight:bold;
+}
+
 .treeView .ui-accordion .ui-accordion-content {
     padding-top: 0.5em;
 }
@@ -1092,4 +1100,4 @@ body *:not(img) {
     width:100%;
     height: 100%;
     z-index: -1;
-}
\ No newline at end of file
+}
diff --git a/public/admin/skins/retro/global.css b/public/admin/skins/retro/global.css
index 255ed3fc6943971849182b10f9ecee866a58f36d..2199e2a1d5c5f2376db28203d32aa00a24a55dad 100755
--- a/public/admin/skins/retro/global.css
+++ b/public/admin/skins/retro/global.css
@@ -279,6 +279,16 @@ table#suggestions td:last-child a {
     min-width: 100px;
 }
 
+
+.modules table.models .group_title {
+    background-color:var(--nav-background);
+    color:white;
+    font-size:120%;
+    padding:2px 10px;
+    font-weight:bold;
+}
+
+
 .treeView .ui-accordion .ui-accordion-content {
     padding-top: 0.5em;
 }
@@ -903,4 +913,4 @@ body .warning {
     padding: 1ex 1em;
     overflow: auto;
     max-height: 300px;
-}
\ No newline at end of file
+}
diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php
index c5338f69a40f5a750c11c3d8efbc5154d162a0cf..56f0ae8961788233999ca0dd583a863244f6b30d 100644
--- a/tests/db/UpgradeDBTest.php
+++ b/tests/db/UpgradeDBTest.php
@@ -239,6 +239,12 @@ abstract class UpgradeDBTestCase extends PHPUnit_Framework_TestCase {
   }
 
 
+  protected function dropFieldFrom($table, $field) {
+    return $this->silentQuery(sprintf('alter table %s drop column %s',
+                                      $table, $field));
+  }
+
+
   protected function dropIndexedFieldFrom($table, $field) {
     return $this->silentQuery('ALTER TABLE ' . $table . ' '
                               . 'DROP KEY ' . $field . ', '
@@ -3322,4 +3328,19 @@ class UpgradeDB_389_Test extends UpgradeDBTestCase {
   public function tableOuverturesShouldContainsColumnMaxPerPeriodApresMidiAsInt(){
     $this->assertFieldType('ouvertures', 'max_per_period_apres_midi', 'int(11)');
   }
+}
+
+
+
+
+class UpgradeDB_390_Test extends UpgradeDBTestCase {
+  public function prepare() {
+    $this->dropFieldFrom('drive_checkout', 'created_at');
+  }
+
+
+  /** @test */
+  public function tableDriveCheckoutShouldContainsCreatedAtColumn() {
+    $this->assertFieldType('drive_checkout', 'created_at', 'datetime');
+  }
 }
\ No newline at end of file
diff --git a/tests/scenarios/DriveCheckOut/DriveCheckOutBookingTest.php b/tests/scenarios/DriveCheckOut/DriveCheckOutBookingTest.php
index 8528acf419118f746ba97a1d1352661956dd7bdd..60be3e417c4c12447e71313b5139d0ce07ffa2e5 100644
--- a/tests/scenarios/DriveCheckOut/DriveCheckOutBookingTest.php
+++ b/tests/scenarios/DriveCheckOut/DriveCheckOutBookingTest.php
@@ -60,6 +60,7 @@ abstract class DriveCheckOutBookingTestCase extends AbstractControllerTestCase {
 
     $timesource = new TimeSourceForTest('2020-05-05 11:30');
     Class_DriveCheckout_Plan::setTimeSource($timesource);
+    Class_DriveCheckout::setTimeSource($timesource);
 
     $this->_mail_transport = new MockMailTransport();
     Zend_Mail::setDefaultTransport($this->_mail_transport);
@@ -72,6 +73,7 @@ abstract class DriveCheckOutBookingTestCase extends AbstractControllerTestCase {
 
   public function tearDown() {
     Class_DriveCheckout_Plan::setTimeSource(null);
+    Class_DriveCheckout::setTimeSource(null);
     Class_Systeme_ModulesAccueil::reset();
     parent::tearDown();
   }
@@ -101,6 +103,7 @@ abstract class DriveCheckOutBookingTestCase extends AbstractControllerTestCase {
                  'closed_on_holidays' => false,
                  'enable_drive' => 1,
                  'horaire' => 'Ouvert tous les lundis, mardi et mercedi',
+                 'mail' => 'emilie@hotel-dieu.fr',
                  'ouvertures' => [
                                   Class_Ouverture::chaqueLundi('00:00', '00:00', '12:00', '18:00')
                                   ->beDrive(),
@@ -118,7 +121,12 @@ abstract class DriveCheckOutBookingTestCase extends AbstractControllerTestCase {
     $this->fixture('Class_Bib',
                    ['id' => 3,
                     'enable_drive' => 1,
-                    'libelle' => 'Mauricette-Rafin']);
+                    'libelle' => 'Mauricette-Rafin',
+                    'ouvertures' => [
+                                     Class_Ouverture::chaqueMardi('00:00', '00:00', '12:00', '18:00')
+                                     ->beDrive()
+                    ]
+                   ]);
 
     $this->fixture('Class_Bib',
                    ['id' => 4,
@@ -233,6 +241,15 @@ class DriveCheckOutUserNotificationsTest extends DriveCheckOutBookingTestCase {
   }
 
 
+  /** @test */
+  public function withoutHoldsInDriveShouldMarcusNotificationsShouldNotContainsLinkToDriveCheckoutPlan() {
+    Class_Bib::find(1)->setEnableDrive(false)->save();
+    Class_Bib::find(2)->setEnableDrive(false)->save();
+    $this->_marcus->registerNotificationsOn($this);
+    $this->assertEmpty($this->_actions);
+  }
+
+
   /** @test */
   public function withDisabledDriveMarcusNotificationActionsShouldNotContainsLinkToDriveCheckoutPlan() {
     Class_AdminVar::set('ENABLE_DRIVE_CHECKOUT', 0);
@@ -310,6 +327,29 @@ class DriveCheckOutBookingPlanTest extends DriveCheckOutBookingTestCase {
 
 
 
+class DriveCheckOutBookingPlanWithoutHoldCheckTest extends DriveCheckOutBookingTestCase {
+  public function setUp() {
+    parent::setUp();
+    Class_AdminVar::set('DRIVE_DISABLE_HOLD_CHECK', 1);
+    $this->dispatch('/opac/drive-checkout/plan');
+  }
+
+
+  /** @test */
+  public function pageShouldContainsLinkToChooseMauricette() {
+    $this->assertXPath('//a[contains(@href, "/plan/id_bib/3")]');
+  }
+
+
+  /** @test */
+  public function pageShouldNotContainsLinkToChooseLeTurbomoteur() {
+    $this->assertNotXPath('//a[contains(@href, "/plan/id_bib/4")]');
+  }
+}
+
+
+
+
 class DriveCheckOutBookingPlanWithFuturExistingTest extends DriveCheckOutBookingTestCase {
   public function setUp() {
     parent::setUp();
@@ -341,7 +381,7 @@ class DriveCheckOutBookingPlanWithFuturExistingTest extends DriveCheckOutBooking
   /** @test */
   public function pageShouldContainsLinkToDeletePlannedCheckout() {
     $this->assertXPathContentContains('//a[contains(@href, "/drive-checkout/delete/id/2")]',
-                                      'Supprimer');
+                                      'Annuler ou replanifier');
   }
 
 
@@ -356,6 +396,8 @@ class DriveCheckOutBookingPlanWithFuturExistingTest extends DriveCheckOutBooking
 
 
 class DriveCheckOutBookingPlanDeleteExistingTest extends DriveCheckOutBookingTestCase {
+  protected $_mail;
+
   public function setUp() {
     parent::setUp();
 
@@ -366,6 +408,7 @@ class DriveCheckOutBookingPlanDeleteExistingTest extends DriveCheckOutBookingTes
                     'start_at' => '2020-05-12 09:00:00']);
 
     $this->dispatch('/opac/drive-checkout/delete/id/2');
+    $this->_mail = $this->_mail_transport->sent_mail;
   }
 
 
@@ -379,6 +422,82 @@ class DriveCheckOutBookingPlanDeleteExistingTest extends DriveCheckOutBookingTes
   public function notificationShouldContainsDeletionDetails() {
     $this->assertFlashMessengerContentContains('Le retrait de vos documents de Albert Camus planifié pour le mardi 12 mai entre 09h00 et 09h30 a été supprimé.');
   }
+
+
+  /** @test */
+  public function mailShouldBeSent() {
+    $this->assertNotNull($this->_mail);
+  }
+
+
+  /** @test */
+  public function mailSubjectShouldBeConfirmationDeRdv() {
+    $this->assertContains('Suppression de votre rendez-vous à Albert Camus',
+                          quoted_printable_decode($this->_mail->getSubject()));
+  }
+
+
+  /** @test */
+  public function mailBodyShouldContainsCheckoutDetails() {
+    $this->assertContains('le mardi 12 mai entre 09h00 et 09h30',
+                          quoted_printable_decode($this->_mail->getBodyHtml(true)));
+  }
+
+
+  /** @test */
+  public function mailFirstRecipientShouldBeMarus() {
+    $this->assertEquals('mm@any-serveur.eu', $this->_mail->getRecipients()[0]);
+  }
+
+
+  /** @test */
+  public function mailProfilShouldBeInBcc() {
+    $this->assertEquals('<zemail@mabib.st>', $this->_mail->getHeaders()['Bcc'][0]);
+  }
+
+
+  /** @test */
+  public function mailFromShouldBeMailProfil() {
+    $this->assertEquals('zemail@mabib.st', $this->_mail->getFrom());
+  }
+
+
+  /** @test */
+  public function mailShouldHaveAttachment() {
+    $this->assertTrue($this->_mail->hasAttachments);
+  }
+
+
+  /** @test */
+  public function mailShouldHaveCalendarIcsPart() {
+    $part = (new Storm_Collection($this->_mail->getParts()))
+      ->detect(function($each)
+               {
+                 return Class_ICal_DriveCheckout::MIME_TYPE === $each->type
+                   && 'calendar.ics' === $each->filename;
+               });
+
+    $this->assertNotNull($part);
+    return base64_decode($part->getContent());
+  }
+
+
+  /**
+   * @test
+   * @depends mailShouldHaveCalendarIcsPart
+   */
+  public function calendarMethodShouldBeCancel($ical) {
+    $this->assertContains('METHOD:CANCEL', $ical);
+  }
+
+
+  /**
+   * @test
+   * @depends mailShouldHaveCalendarIcsPart
+   */
+  public function eventStatusShouldBeCancelled($ical) {
+    $this->assertContains('STATUS:CANCELLED', $ical);
+  }
 }
 
 
@@ -690,7 +809,8 @@ class DriveCheckOutBookingPlanBibHotelDieuAt2020_05_12_09_00Test
   public function checkoutShouldBePlanned() {
     $this->assertNotNull(Class_DriveCheckout::findFirstBy(['user_id' => $this->_marcus->getId(),
                                                            'library_id' => 1,
-                                                           'start_at' => '2020-05-12 09:00:00']));
+                                                           'start_at' => '2020-05-12 09:00:00',
+                                                           'created_at' => '2020-05-05 11:30:00']));
   }
 
 
@@ -733,14 +853,14 @@ class DriveCheckOutBookingPlanBibHotelDieuAt2020_05_12_09_00Test
 
 
   /** @test */
-  public function mailSiteShouldBeInBcc() {
-    $this->assertContains('zemail@mabib.st', $this->_mail->getHeaders()['Bcc']);
+  public function mailSiteHotelDieuShouldBeInBcc() {
+    $this->assertEquals('<emilie@hotel-dieu.fr>', $this->_mail->getHeaders()['Bcc'][0]);
   }
 
 
   /** @test */
-  public function mailFromShouldBeMailSite() {
-    $this->assertEquals('zemail@mabib.st', $this->_mail->getFrom());
+  public function mailFromShouldBeMailSiteHotelDieu() {
+    $this->assertEquals('emilie@hotel-dieu.fr', $this->_mail->getFrom());
   }
 
 
@@ -766,6 +886,34 @@ class DriveCheckOutBookingPlanBibHotelDieuAt2020_05_12_09_00Test
 
 
 
+class DriveCheckOutBookingPlanBibHotelDieuWithoutMailAt2020_05_12_09_00Test
+  extends DriveCheckOutBookingTestCase {
+  protected $_mail;
+
+  public function setUp() {
+    parent::setUp();
+    Class_Bib::find(1)->setMail('')->save();
+    $this->postDispatch('/opac/drive-checkout/plan/id_bib/1/checkout_date/2020-05-12',
+                        ['checkout_time' => '09:00']);
+    $this->_mail = $this->_mail_transport->sent_mail;
+  }
+
+
+  /** @test */
+  public function mailSiteHotelDieuShouldBeInBcc() {
+    $this->assertEquals('<zemail@mabib.st>', $this->_mail->getHeaders()['Bcc'][0]);
+  }
+
+
+  /** @test */
+  public function mailFromShouldBeMailSiteHotelDieu() {
+    $this->assertEquals('zemail@mabib.st', $this->_mail->getFrom());
+  }
+}
+
+
+
+
 class DriveCheckOutBookingPlanBibHotelDieuAt2020_05_12_05_00Test
   extends DriveCheckOutBookingTestCase {
 
@@ -833,4 +981,42 @@ class DriveCheckOutBookingPlanWithoutHoldsTest extends DriveCheckOutBookingTestC
   public function pageShouldContainsNoHoldMessage() {
     $this->assertXPathContentContains('//div//p', 'Aucun document à retirer pour l\'instant');
   }
+}
+
+
+
+
+class DriveCheckOutBookingPlanWithOnlyOnePossibleLibrary
+  extends DriveCheckOutBookingTestCase {
+
+  public function setUp() {
+    parent::setUp();
+
+    $fiche = (new Class_WebService_SIGB_Emprunteur('10', 'Marcus'))
+      ->reservationsAddAll([$this->_holdIn(1)]);
+
+    $this->_marcus
+      ->setFicheSigb(['type_comm' => Class_IntBib::COM_NANOOK,
+                      'fiche' => $fiche]);
+  }
+
+
+  /** @test */
+  public function withoutFuturCheckoutshouldRedirectToDateChoiceOfFirstLibrary() {
+    $this->dispatch('/opac/drive-checkout/plan');
+    $this->assertRedirectTo('/drive-checkout/plan/id_bib/1');
+  }
+
+
+  /** @test */
+  public function withFutureCheckoutShouldNotRedirect() {
+    $this->fixture('Class_DriveCheckout',
+                   ['id' => 2,
+                    'library_id' => 1,
+                    'user_id' => $this->_marcus->getId(),
+                    'start_at' => '2020-05-12 09:00:00']);
+
+    $this->dispatch('/opac/drive-checkout/plan');
+    $this->assertNotRedirect();
+  }
 }
\ No newline at end of file
diff --git a/tests/scenarios/DriveCheckOut/DriveCheckoutAdminControllerTest.php b/tests/scenarios/DriveCheckOut/DriveCheckoutAdminControllerTest.php
index a19005ead070fff82c2afee64f2c795334e21cc1..5694ab45f363314f946b1079b0cc0a8269923438 100644
--- a/tests/scenarios/DriveCheckOut/DriveCheckoutAdminControllerTest.php
+++ b/tests/scenarios/DriveCheckOut/DriveCheckoutAdminControllerTest.php
@@ -76,6 +76,51 @@ abstract class DriveCheckOutAdminControllerTestCase extends Admin_AbstractContro
                                'idabon' => 'A123',
                                'bib' => $lib_hotel_dieu]);
 
+
+    $this->fixture('Class_CodifSection',
+                   ['id' => 3,
+                    'libelle' => 'Jeunesse']);
+
+    $this->fixture('Class_CodifEmplacement',
+                   ['id' => 2,
+                    'libelle' => 'BD',
+                    'regles' => '995$e=bd']);
+
+    $holds = [Class_WebService_SIGB_Reservation::newInstanceWithEmptyExemplaire()
+              ->setExemplaireOPAC($this->fixture('Class_Exemplaire',
+                                                 ['id' => 2,
+                                                  'code_barres' => 'tintin123',
+                                                  'notice' => $this->fixture('Class_Notice',
+                                                                             ['id' => 123,
+                                                                              'titre_principal' => 'Tintin à Dole']),
+                                                  'emplacement' => 2,
+                                                  'section' => 3,
+                                                  'cote' => 'BD2']))
+              ->setBibliotheque($lib_camus->getLibelle())
+              ->setCodeBarre('tintin123')
+              ->setTitre('Tintin à Dole')
+              ->setEtat('On le cherche')
+              ->setWaitingToBePulled(),
+
+              Class_WebService_SIGB_Reservation::newInstanceWithEmptyExemplaire()
+              ->setBibliotheque($lib_camus->getLibelle())
+              ->setCodeBarre('milou123')
+              ->setTitre('Milou à Dole')
+              ->setWaitingToBePulled(),
+
+              Class_WebService_SIGB_Reservation::newInstanceWithEmptyExemplaire()
+              ->setBibliotheque($lib_maurissette->getLibelle())
+              ->setCodeBarre('tournesol123')
+              ->setEtat('Disponible')
+              ->setTitre('Tournesol à Maurissette')
+              ->setWaitingToBePulled(),
+
+              Class_WebService_SIGB_Reservation::newInstanceWithEmptyExemplaire()
+              ->setBibliotheque($lib_camus->getLibelle())
+              ->setCodeBarre('dupont123')
+              ->setTitre('Dupont à Dole')
+    ];
+
     $maurice = $this->fixture('Class_Users',
                               ['id' => 4,
                                'login' => 'maurice',
@@ -85,76 +130,48 @@ abstract class DriveCheckOutAdminControllerTestCase extends Admin_AbstractContro
                                'bib' => $lib_hotel_dieu])
                     ->setFicheSigb(['type_comm' => Class_IntBib::COM_NANOOK,
                                     'fiche' => (new Class_WebService_SIGB_Emprunteur(4, 'Maurice'))
-                                    ->reservationsAddAll(
-                                                         [Class_WebService_SIGB_Reservation::newInstanceWithEmptyExemplaire()
-                                                          ->setExemplaireOPAC($this->fixture('Class_Exemplaire',
-                                                                                             ['id' => 2,
-                                                                                              'code_barres' => 'tintin123',
-                                                                                              'notice' => $this->fixture('Class_Notice',
-                                                                                                                         ['id' => 123,
-                                                                                                                          'titre_principal' => 'Tintin à Dole']),
-                                                                                              'cote' => 'BD2']))
-                                                          ->setBibliotheque($lib_camus->getLibelle())
-                                                          ->setCodeBarre('tintin123')
-                                                          ->setTitre('Tintin à Dole')
-                                                          ->setEtat('On le cherche')
-                                                          ->setWaitingToBePulled(),
-
-                                                          Class_WebService_SIGB_Reservation::newInstanceWithEmptyExemplaire()
-                                                          ->setBibliotheque($lib_camus->getLibelle())
-                                                          ->setCodeBarre('milou123')
-                                                          ->setTitre('Milou à Dole')
-                                                          ->setWaitingToBePulled(),
-
-                                                          Class_WebService_SIGB_Reservation::newInstanceWithEmptyExemplaire()
-                                                          ->setBibliotheque($lib_maurissette->getLibelle())
-                                                          ->setCodeBarre('tournesol123')
-                                                          ->setEtat('Disponible')
-                                                          ->setTitre('Tournesol à Maurissette')
-                                                          ->setWaitingToBePulled(),
-
-                                                          Class_WebService_SIGB_Reservation::newInstanceWithEmptyExemplaire()
-                                                          ->setBibliotheque($lib_camus->getLibelle())
-                                                          ->setCodeBarre('dupont123')
-                                                          ->setTitre('Dupont à Dole')
-                                                         ])]);
-
+                                    ->reservationsAddAll($holds)]);
 
     $this->fixture('Class_DriveCheckout',
                    ['id' => 1,
                     'user' => $emilie,
                     'library' => $lib_hotel_dieu,
-                    'start_at' => '2020-05-12 09:00:00']);
+                    'start_at' => '2020-05-12 09:00:00',
+                    'created_at' => '0000-00-00 00:00:00']);
 
     $this->fixture('Class_DriveCheckout',
                    ['id' => 2,
                     'user' => $bernard,
                     'library' => $lib_hotel_dieu,
-                    'start_at' => '2020-05-12 14:00:00']);
+                    'start_at' => '2020-05-12 14:00:00',
+                    'created_at' => '2020-05-02 23:46:33']);
 
     $this->fixture('Class_DriveCheckout',
                    ['id' => 3,
                     'user' => $maurice,
                     'library' => $lib_camus,
-                    'start_at' => '2020-05-14 09:00:00']);
+                    'start_at' => '2020-05-14 09:00:00',
+                    'created_at' => '0000-00-00 00:00:00']);
 
     $this->fixture('Class_DriveCheckout',
                    ['id' => 4,
                     'user' => $emilie,
                     'library' => $lib_camus,
-                    'start_at' => '2020-05-14 09:15:00']);
+                    'start_at' => '2020-05-14 09:15:00',
+                    'created_at' => '2020-05-03 19:32:55']);
 
     $this->fixture('Class_DriveCheckout',
                    ['id' => 5,
                     'user' => $bernard,
                     'library' => $lib_camus,
-                    'start_at' => '2020-05-15 10:00:00']);
-
+                    'start_at' => '2020-05-15 10:00:00',
+                    'created_at' => '0000-00-00 00:00:00']);
   }
 }
 
 
 
+
 class DriveCheckoutAdminControllerAdminVarTest extends DriveCheckOutAdminControllerTestCase {
   /** @test */
   public function withAdminVarEnableDriveFalseShouldNotDisplayMenuEntry() {
@@ -246,21 +263,27 @@ class DriveCheckoutAdminControllerListHotelDieuTest extends DriveCheckOutAdminCo
   }
 
 
+  /** @test */
+  public function pageShouldContainsInputForDaysValueOne() {
+    $this->assertXPath('//input[@name="days"][@type="number"][@value="1"][@onchange]');
+  }
+
+
   /** @test */
   public function pageShouldContainsTableWithCheckout2020_05_12_at_9_00_For_Emilie() {
-    $this->assertXPath('//table//td[text()="09:00"]/following-sibling::td[text()="A121"]/following-sibling::td[text()="emilie"]');
+    $this->assertXPath('//table//td[text()="09:00"]/following-sibling::td[text()="A121"]/following-sibling::td[text()="emilie"]/following-sibling::td[text()="n/a"]');
   }
 
 
   /** @test */
   public function pageShouldContainsTableWithCheckout2020_05_12_at_14_00_For_Bernard() {
-    $this->assertXPath('//table//td[text()="14:00"]/following-sibling::td[text()="A123"]/following-sibling::td[text()="bernard"]');
+    $this->assertXPath('//table//td[text()="14:00"]/following-sibling::td[text()="A123"]/following-sibling::td[text()="bernard"]/following-sibling::td[text()="02 mai à 23h46"]');
   }
 
 
   /** @test */
   public function pageShouldContainsTwoCheckouts() {
-    $this->assertXPathCount('//table[@id="checkouts"]/tbody/tr', 2);
+    $this->assertXPathCount('//table[@id="checkouts"]/tbody/tr[not(@class="group_title")]', 2);
   }
 }
 
@@ -306,7 +329,7 @@ class DriveCheckoutAdminControllerListCamusOnMayFourteenthTest extends DriveChec
 
   /** @test */
   public function pageShouldContainsTwoCheckouts() {
-    $this->assertXPathCount('//table[@id="checkouts"]/tbody/tr', 2);
+    $this->assertXPathCount('//table[@id="checkouts"]/tbody/tr[not(@class="group_title")]', 2);
   }
 
 
@@ -325,7 +348,39 @@ class DriveCheckoutAdminControllerListCamusOnMayFourteenthTest extends DriveChec
 
 
 
-class DriveCheckoutAdminControllerExportCSVCamusOnMayFourteenthTest extends DriveCheckOutAdminControllerTestCase {
+class DriveCheckoutAdminControllerListCamusOnMayFourteenthDaysSevenTest extends DriveCheckOutAdminControllerTestCase {
+  public function setUp() {
+    parent::setUp();
+    $this->dispatch('/admin/drive-checkout/list/id_bib/2/date/2020-05-14/days/7');
+  }
+
+
+  /** @test */
+  public function pageShouldContainsThreeCheckouts() {
+    $this->assertXPathCount('//table[@id="checkouts"]/tbody/tr[not(@class="group_title")]',
+                            3,
+                            $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function pageShouldContainsGroupTitleForMayFourteenth() {
+    $this->assertXPathContentContains('//table//tr[@class="group_title"]', 'jeudi 14 mai 2020');
+  }
+
+
+  /** @test */
+  public function pageShouldContainsInputForDaysValueSeven() {
+    $this->assertXPath('//input[@name="days"][@type="number"][@value="7"][@onchange]');
+  }
+}
+
+
+
+
+class DriveCheckoutAdminControllerExportCSVCamusOnMayFourteenthTest
+  extends DriveCheckOutAdminControllerTestCase {
+
   public function setUp() {
     parent::setUp();
     $this->dispatch('/admin/drive-checkout/list-csv/id_bib/2/date/2020-05-14');
@@ -342,9 +397,9 @@ class DriveCheckoutAdminControllerExportCSVCamusOnMayFourteenthTest extends Driv
 
   /** @test */
   public function csvShouldContainsCheckouts() {
-    $this->assertEquals("Heure;Carte;Abonné\n"
-                        ."09:00;A124;maurice\n"
-                        ."09:15;A121;emilie\n",
+    $this->assertEquals("Jour;Heure;Carte;Abonné;\"Créé le\"\n"
+                        ."\"14 mai\";09:00;A124;maurice;\n"
+                        ."\"14 mai\";09:15;A121;emilie;\"2020-05-03 19:32:55\"\n",
                         $this->_response->getBody());
   }
 }
@@ -352,7 +407,9 @@ class DriveCheckoutAdminControllerExportCSVCamusOnMayFourteenthTest extends Driv
 
 
 
-class DriveCheckoutAdminControllerListHoldsForCheckoutThreeTest extends DriveCheckOutAdminControllerTestCase {
+class DriveCheckoutAdminControllerListHoldsForCheckoutThreeTest
+  extends DriveCheckOutAdminControllerTestCase {
+
   public function setUp() {
     parent::setUp();
     $this->dispatch('/admin/drive-checkout/list-holds/id/3');
@@ -371,6 +428,12 @@ class DriveCheckoutAdminControllerListHoldsForCheckoutThreeTest extends DriveChe
   }
 
 
+  /** @test */
+  public function tableShouldContainsHoldOnTintinWithSectionAndEmplacement() {
+    $this->assertXPath('//table//td[text()="Jeunesse"]/following-sibling::td[text()="BD"]/following-sibling::td[text()="BD2"]');
+  }
+
+
   /** @test */
   public function tableShouldNotContainsHoldOnTournesol() {
     $this->assertNotXPath('//table//td[contains(text(),"Tournesol")]');
@@ -432,10 +495,10 @@ class DriveCheckoutAdminControllerExportItemsCSVCamusOnMayFourteenthTest extends
 
   /** @test */
   public function csvShouldContainsTintinMilouAndDupontItems() {
-    $this->assertEquals("Jour;Heure;Carte;Abonné;Cote;Code-barres;Titre\n"
-                        . "\"14 mai\";09:00;A124;maurice;BD2;tintin123;\"Tintin à Dole\"\n"
-                        . "\"14 mai\";09:00;A124;maurice;;milou123;\"Milou à Dole\"\n"
-                        . "\"14 mai\";09:00;A124;maurice;;dupont123;\"Dupont à Dole\"\n",
+    $this->assertEquals("Jour;Heure;Carte;Abonné;\"Créé le\";Section;Emplacement;Cote;Code-barres;Titre\n"
+                        . "\"14 mai\";09:00;A124;maurice;;Jeunesse;BD;BD2;tintin123;\"Tintin à Dole\"\n"
+                        . "\"14 mai\";09:00;A124;maurice;;;;;milou123;\"Milou à Dole\"\n"
+                        . "\"14 mai\";09:00;A124;maurice;;;;;dupont123;\"Dupont à Dole\"\n",
                         $this->_response->getBody());
   }
 }