diff --git a/application/modules/admin/controllers/NewsletterController.php b/application/modules/admin/controllers/NewsletterController.php
index 99e5a7c2a1a3fd6d89e968b610f5d5b163d73c83..95fddc32a97a5af45ae265faeb4f4b4030c0e353 100644
--- a/application/modules/admin/controllers/NewsletterController.php
+++ b/application/modules/admin/controllers/NewsletterController.php
@@ -112,8 +112,13 @@ function sendNewsletterClick(event) {
 
 
   public function sendAction() {
-    if ($newsletter = Class_Newsletter::find((int)$this->_getParam('id')))
-      $newsletter->send();
+    if (!$newsletter = Class_Newsletter::find((int)$this->_getParam('id'))) {
+      $this->_helper->notify($this->_('Envoi impossible : Newsletter inconnue'));
+      return $this->_redirectToIndex();
+    }
+
+    if (!$newsletter->send())
+      $this->_helper->notify($this->_('Envoi impossible : erreur à la création de la commande d\'envoi'));
 
     $this->_redirectToIndex();
   }
@@ -272,4 +277,12 @@ function sendNewsletterClick(event) {
                                        'id' => $model->getId()], null, true),
                      ['prependBase' => false]);
   }
+
+
+  public function showStatusAction() {
+    $this->view->titre = $this->_('Détails de l\'erreur');
+    $this->view->model = Class_Newsletter_Dispatch::find((int)$this->_getParam('id'));
+    if (!$this->view->model)
+      throw new Zend_Controller_Action_Exception($this->_('Désolé, cette page n\'existe pas'), 404);
+  }
 }
\ No newline at end of file
diff --git a/application/modules/admin/views/scripts/newsletter/show-status.phtml b/application/modules/admin/views/scripts/newsletter/show-status.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..bd088d639a92460e982fc092562f19475b2445ff
--- /dev/null
+++ b/application/modules/admin/views/scripts/newsletter/show-status.phtml
@@ -0,0 +1,2 @@
+<?php
+echo $this->tag('p', nl2br($this->model->getErrorMessage()));
diff --git a/cosmogramme/php/_init.php b/cosmogramme/php/_init.php
index b567a79e5386d5ee557ad2581425fc6778956231..5842c641e270bb809fa84d2d297cc099f2cd6173 100644
--- a/cosmogramme/php/_init.php
+++ b/cosmogramme/php/_init.php
@@ -1,7 +1,7 @@
 <?php
 error_reporting(E_ERROR | E_PARSE);
 
-define("PATCH_LEVEL","312");
+define("PATCH_LEVEL","313");
 
 define("APPLI","cosmogramme");
 define("COSMOPATH", "/var/www/html/vhosts/opac2/www/htdocs");
diff --git a/cosmogramme/sql/patch/patch_313.php b/cosmogramme/sql/patch/patch_313.php
new file mode 100644
index 0000000000000000000000000000000000000000..6f2409fa08ce6770a38fac39ff6023c1b3f5fc22
--- /dev/null
+++ b/cosmogramme/sql/patch/patch_313.php
@@ -0,0 +1,5 @@
+<?php
+try {
+  Zend_Db_Table_Abstract::getDefaultAdapter()
+    ->query('alter table newsletter_dispatch add error longtext');
+} catch(Exception $e) {}
\ No newline at end of file
diff --git a/library/Class/Batch/SendNewsletters.php b/library/Class/Batch/SendNewsletters.php
index 5da4957ddde360faf27c310ec09b3a9fa664f8d4..d20de23df186ef0af68fc4fa096ecb866c2830ad 100644
--- a/library/Class/Batch/SendNewsletters.php
+++ b/library/Class/Batch/SendNewsletters.php
@@ -55,16 +55,36 @@ class Class_Batch_SendNewsletters extends Class_Batch_Abstract {
 
 
   public function run() {
-    $this->getCommand()
-         ->exec('/usr/bin/php -f '
-                . realpath(dirname(__FILE__)) . '/../../../scripts/sendNewsletter.php '
-                . $this->getExecParams()
-                . ' > /dev/null &');
+    $shell = '/usr/bin/php -f '
+      . realpath(dirname(__FILE__)) . '/../../../scripts/sendNewsletter.php '
+      . $this->getExecParams()
+      . ' > /dev/null & echo $!';
+
+    $command = $this->getCommand();
+    $command->exec($shell);
+    if ((!$output = $command->getOutput())
+        || !is_array($output)
+        || !$output[0]) {
+      $this->_endWithError('Unable to run ' . $shell);
+      return false;
+    }
 
-    return $this;
+    $command->exec('ps '. $output[0]);
+
+    if (!$success = 0 === $command->getReturnVar())
+      $this->_endWithError('Unable to run ' . $shell);
+
+    return $success;
   }
 
 
+  protected function _endWithError($message) {
+    $this->_dispatch
+      ->setError(json_encode(['message' => $message]))
+      ->beEnded()
+      ;
+  }
+
 
   public function sendOne($email) {
     if (!$this->_dispatch)
@@ -136,6 +156,11 @@ class Class_Batch_SendNewsletters extends Class_Batch_Abstract {
   }
 
 
+  protected function _checksum($params) {
+    return sha1(serialize($params));
+  }
+
+
   public function getTimeLimit() {
     if (!$this->_time_limit)
       $this->_time_limit = Class_Systeme_TimeLimit::getInstance();
diff --git a/library/Class/Newsletter.php b/library/Class/Newsletter.php
index be96e8d56dc275005074b22d06e0d73d5c5540a1..c030c17eac4689748eedb57bb813aa73e5eaf63a 100644
--- a/library/Class/Newsletter.php
+++ b/library/Class/Newsletter.php
@@ -99,10 +99,15 @@ class Class_Newsletter extends Storm_Model_Abstract {
 
 
   public function send() {
-    if (!$dispatch = $this->getLastDispatchInProgress())
-      $dispatch = Class_Newsletter_Dispatch::newFrom($this);
+    return (new Class_Batch_SendNewsletters($this->_getOrCreateDispatchToRun()))
+      ->run();
+  }
+
 
-    return (new Class_Batch_SendNewsletters($dispatch))->run();
+  protected function _getOrCreateDispatchToRun() {
+    return ($dispatch = $this->getLastDispatch()) && $dispatch->isRunnable()
+      ? $dispatch->resetError()
+      : Class_Newsletter_Dispatch::newFrom($this);
   }
 
 
@@ -404,11 +409,17 @@ class Class_Newsletter extends Storm_Model_Abstract {
     return $this->getTitre();
   }
 
+
   public function getLastDispatchInProgress() {
     return Class_Newsletter_Dispatch::lastDispatchInProgressFor($this);
   }
 
 
+  public function getLastDispatch() {
+    return Class_Newsletter_Dispatch::lastDispatchFor($this);
+  }
+
+
   public function getSortedRecipientsByDedicatedAndLabel() {
     $groups = $this->getRecipientsGroups();
     usort(
diff --git a/library/Class/Newsletter/Dispatch.php b/library/Class/Newsletter/Dispatch.php
index 9cd117e1f585348e83e5c5195ded5e1e875662a7..151d7139f9c3c8e9c225f8f25264e41bfd1cdee9 100644
--- a/library/Class/Newsletter/Dispatch.php
+++ b/library/Class/Newsletter/Dispatch.php
@@ -46,6 +46,13 @@ class Newsletter_DispatchLoader extends Storm_Model_Loader {
                                                    'ended_on' => null]);
 
   }
+
+
+  public function lastDispatchFor($newsletter) {
+    return Class_Newsletter_Dispatch::findFirstBy(['newsletter_id' => $newsletter->getId(),
+                                                   'order' => 'created_on desc']);
+
+  }
 }
 
 
@@ -69,7 +76,8 @@ class Class_Newsletter_Dispatch extends Storm_Model_Abstract {
                           'users' => ['through' => 'dispatch_users']];
 
   protected $_default_attribute_values = ['collected' => 0,
-                                          'ended_on' => null];
+                                          'ended_on' => null,
+                                          'error' => null];
 
 
   public function beforeSave() {
@@ -114,10 +122,10 @@ class Class_Newsletter_Dispatch extends Storm_Model_Abstract {
       $params = ['dispatch_id' => $this->getId(),
                  'user_id' => $model->getId()];
 
-      if (Class_Newsletter_DispatchUser::findFirstBy($params))
-        return;
-      if (in_array( $model->getMail(),$blacklist_mails))
+      if (Class_Newsletter_DispatchUser::findFirstBy($params)
+          || in_array($model->getMail(), $blacklist_mails))
         return;
+
       Class_Newsletter_DispatchUser::newInstance(['dispatch_id' => $this->getId(),
                                                   'user_id' => $model->getId(),
                                                   'mail' => $model->getMail()])
@@ -146,4 +154,44 @@ class Class_Newsletter_Dispatch extends Storm_Model_Abstract {
     $this->getNewsletter()->setLastDistributionDateWithFormat();
     return $this;
   }
+
+
+  public function getErrorMessage() {
+    if (!$this->hasError()
+        || (!$json = json_decode($this->getError(), true))
+        || !array_key_exists('message', $json))
+      return '';
+
+    return $json['message'];
+  }
+
+
+  public function isRunnable() {
+    return $this->isInProgress() || $this->canRetry();
+  }
+
+
+  public function canRetry() {
+    return $this->hasError()
+      && $this->getCollected()
+      && $this->numberOfDoneUsers() < $this->numberOfDispatchUsers();
+  }
+
+
+  public function isInProgress() {
+    return !$this->hasEndedOn();
+  }
+
+
+  public function resetError() {
+    if (!$this->canRetry())
+      return $this;
+
+    $this
+      ->setError(null)
+      ->setEndedOn(null)
+      ->save();
+
+    return $this;
+  }
 }
diff --git a/library/ZendAfi/View/Helper/GetSendProgressJsonFor.php b/library/ZendAfi/View/Helper/GetSendProgressJsonFor.php
index 2594f408ce1444c54eac1dac4d672621f31d15c2..c65a3d540cbeebbe080ef3e285701f49211e00a5 100644
--- a/library/ZendAfi/View/Helper/GetSendProgressJsonFor.php
+++ b/library/ZendAfi/View/Helper/GetSendProgressJsonFor.php
@@ -21,27 +21,39 @@
 
 class ZendAfi_View_Helper_getSendProgressJsonFor extends ZendAfi_View_Helper_BaseHelper {
   public function getSendProgressJsonFor($newsletter) {
-    if(!$newsletter)
+    if (!$newsletter)
       return $this->_toStatus($this->_('Newsletter inconnue'));
 
-    ($last_date = DateTime::createFromFormat("Y-m-d H:i:s", $newsletter->getLastDistributionDate()))
-      ? $last_date_text = $this->_toStatus($last_date->format("d/m/Y H:i"))
+    ($last_date = DateTime::createFromFormat('Y-m-d H:i:s', $newsletter->getLastDistributionDate()))
+      ? $last_date_text = $this->_toStatus($last_date->format('d/m/Y H:i'))
       : $last_date_text = $this->_toStatus($this->_('Aucune'));
 
-    if(!$dispatch = $newsletter->getLastDispatchInProgress())
+    if (!$dispatch = $newsletter->getLastDispatch())
       return $last_date_text;
 
-    if($dispatch->getCollected() && (!$dispatch->hasEndedOn()))
-      return array_merge($this->_toStatus($this->_('en cours')),
+    if (!$dispatch->hasEndedOn())
+      return array_merge($this->_toStatus(!$dispatch->getCollected()
+                                          ? $this->_('collecte des destinataires')
+                                          : $this->_('envoi en cours')),
                          ['done' => $dispatch->numberOfDoneUsers(),
                           'total' => $dispatch->numberOfDispatchUsers()]);
 
-    if(!$last_date) {
-      $dispatch->delete();
-      return $last_date_text;
-    }
+    return $dispatch->hasError()
+      ? $this->_toStatus($this->_tag('a', $this->_('Erreur lors de l\'envoi'),
+                                     ['data-popup' => 'true',
+                                      'href' => $this->view->url(['module' => 'admin',
+                                                                  'controller' => 'newsletter',
+                                                                  'action' => 'show-status',
+                                                                  'id' => $dispatch->getId()],
+                                                                 null, true)])
+                         . ($dispatch->getCollected()
+                            ? $this->_(', envoyé à %s sur %s',
+                                       $dispatch->numberOfDoneUsers(),
+                                       $dispatch->numberOfDispatchUsers())
+                            : $this->_(', aucun mail envoyé')))
 
-    return $this->_toStatus($this->_('échec de l\'envoi'));
+      : $this->_toStatus($last_date->format('d/m/Y H:i')
+                         . $this->_(', %s mails envoyés', $dispatch->numberOfDoneUsers()));
   }
 
 
diff --git a/library/ZendAfi/View/Helper/TagProgressBarForNewsletter.php b/library/ZendAfi/View/Helper/TagProgressBarForNewsletter.php
index edb05a1547c30f36aa19335b763fd79fcda07997..61aaf0fa303ba9b6c7ec14ebb9d432ef9b9232ae 100644
--- a/library/ZendAfi/View/Helper/TagProgressBarForNewsletter.php
+++ b/library/ZendAfi/View/Helper/TagProgressBarForNewsletter.php
@@ -25,20 +25,25 @@ class ZendAfi_View_Helper_TagProgressBarForNewsletter extends ZendAfi_View_Helpe
     $data_url = $this->view->url(['module'=>'admin',
                                   'controller' => 'newsletter',
                                   'action' => 'send-progress',
-                                  'id' => $newsletter->getId()],null,true);
+                                  'id' => $newsletter->getId()],
+                                 null, true);
+
     $progress_data = 'function getNewsletterProgress'.$newsletter->getId().'(){$.getJSON("'.$data_url.'", function (data) {'.
-      'if(!data.done || !data.total) {'.
-      '$("#progress_bar_newsletter_'.$newsletter->getId().'").text(data.status); return true;'.
+      '$("#progress_bar_newsletter_'.$newsletter->getId().' span").html(data.status);'.
+      'if(!data.total) {'.
+      '$("#progress_bar_newsletter_'.$newsletter->getId().'").html(data.status);'.
+      'initializePopups();'.
+      'return true;'.
       '}'.
       '$("#progress_bar_newsletter_'.$newsletter->getId().'").progressbar({value: data.done, max: data.total});'.
-      'setTimeout(function() {getNewsletterProgress'.$newsletter->getId().'()}, 100);'.
+      'setTimeout(function() {getNewsletterProgress'.$newsletter->getId().'();}, 100);'.
       '})};';
 
     Class_ScriptLoader::getInstance()
       ->addInlineScript($progress_data)
       ->addJQueryReady('getNewsletterProgress'.$newsletter->getId().'()');
 
-    return $this->_tag('div', '',
+    return $this->_tag('div', $this->_tag('span', ''),
                        ['id' => 'progress_bar_newsletter_' . $newsletter->getId()]);
   }
 }
\ No newline at end of file
diff --git a/scripts/sendNewsletter.php b/scripts/sendNewsletter.php
index 467cf67a308039bc00a7bbdb43bb7a0889458ae5..a90e862f4877919ab885d9686dfe0d932592ecb0 100644
--- a/scripts/sendNewsletter.php
+++ b/scripts/sendNewsletter.php
@@ -22,22 +22,29 @@
 define('BASE_URL', $argv[3]);
 $_SERVER['SERVER_NAME'] = $argv[2];
 $_SERVER['HTTP_HOST'] = $argv[1];
+$dispatch_id = $argv[4];
 
-set_include_path('.'
-								 . PATH_SEPARATOR .realpath(dirname(__FILE__)). '/../library'
-								 . PATH_SEPARATOR .realpath(dirname(__FILE__)). '/../library/storm/zf/library'
-								 . PATH_SEPARATOR .realpath(dirname(__FILE__)). '/../library/storm/src'
-								 . PATH_SEPARATOR .realpath(dirname(__FILE__)).'/../library/Class'
-								 . PATH_SEPARATOR .realpath(dirname(__FILE__)).'/../library/ZendAfi'
-. PATH_SEPARATOR . realpath(dirname(__FILE__)).'/../application/modules'
-. PATH_SEPARATOR . realpath(dirname(__FILE__)).'/application/modules'
-	);
+require_once 'includes.php';
 
-include_once "local.php";
-include_once "fonctions/fonctions.php";
-require_once "Zend/Loader.php";
-require_once "startup.php";
+$bokeh = (new Bokeh_Engine())->warmUp();
+if ('test' != $dispatch_id
+    && (!$dispatch = Class_Newsletter_Dispatch::find($dispatch_id))) {
+  printf("Unkown dispatch %s\n", $dispatch_id);
+  exit(1);
+}
 
-setupOpac();
+$my_shutdown = function() use ($dispatch) {
+  if (!$error = error_get_last())
+    return;
 
-Class_Newsletter_Dispatch::find($argv[4])->sendBy(20);
\ No newline at end of file
+  if (E_ERROR === $error['type'])
+    $dispatch->setError(json_encode($error))
+             ->beEnded();
+};
+
+register_shutdown_function($my_shutdown);
+
+$bokeh->powerOn();
+
+if ($dispatch)
+  $dispatch->sendBy(20);
\ No newline at end of file
diff --git a/tests/application/modules/admin/controllers/NewsletterControllerTest.php b/tests/application/modules/admin/controllers/NewsletterControllerTest.php
index ba176878c36da75407e33f2ea74bcf06fd71796d..44b5a7511761730bb0a84d0fe93fd707689504bb 100644
--- a/tests/application/modules/admin/controllers/NewsletterControllerTest.php
+++ b/tests/application/modules/admin/controllers/NewsletterControllerTest.php
@@ -485,15 +485,15 @@ class Admin_NewsletterControllerSendInProgressActionTest extends Admin_Newslette
     parent::setUp();
 
     $this->_command = $this->mock()
-                           ->whenCalled('exec')
-                           ->answers(true);
+                           ->whenCalled('exec')->answers(true)
+                           ->whenCalled('getOutput')->answers(['999'])
+                           ->whenCalled('getReturnVar')->answers(0);
 
     Class_Batch_SendNewsletters::setCommand($this->_command);
     Class_Newsletter_Dispatch::setTimeSource(new TimeSourceForTest('2016-07-21 11:21:38'));
 
-    $dispatch=Class_Newsletter_Dispatch::newFrom(Class_Newsletter::find(2));
+    $dispatch = Class_Newsletter_Dispatch::newFrom(Class_Newsletter::find(2));
     $dispatch->assertSave();
-    $dispatch->collectRecipients();
 
     $this->dispatch('/admin/newsletter/send/id/2', true);
     $this->_dispatch = Class_Newsletter::find(2)->getDispatchs()[0];
@@ -514,7 +514,108 @@ class Admin_NewsletterControllerSendInProgressActionTest extends Admin_Newslette
 
 
 
-class Admin_NewsletterControllerSendActionTest extends Admin_NewsletterControllerTestCase {
+class Admin_NewsletterControllerSendPreviouslyNotCollectedErrorActionTest
+  extends Admin_NewsletterControllerTestCase {
+
+  protected
+    $_command,
+    $_dispatch;
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->_command = $this->mock()
+                           ->whenCalled('exec')->answers(true)
+                           ->whenCalled('getOutput')->answers(['999'])
+                           ->whenCalled('getReturnVar')->answers(0);
+
+    Class_Batch_SendNewsletters::setCommand($this->_command);
+    Class_Newsletter_Dispatch::setTimeSource(new TimeSourceForTest('2016-07-21 11:21:38'));
+
+    Class_Newsletter_Dispatch::newFrom(Class_Newsletter::find(2))
+      ->setError(json_encode(['message' => 'An error occurred']))
+      ->setEndedOn('2012-10-26 12:03:45')
+      ->assertSave();
+
+    $this->dispatch('/admin/newsletter/send/id/2', true);
+  }
+
+
+  public function tearDown() {
+    Class_Batch_SendNewsletters::setCommand(null);
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function shouldHaveCreateAnotherDispatch() {
+    $this->assertEquals(2, Class_Newsletter::find(2)->numberOfDispatchs());
+  }
+
+
+  /** @test */
+  public function shouldHaveADispatchInProgress() {
+    $this->assertNotNull(Class_Newsletter::find(2)->getLastDispatchInProgress());
+  }
+}
+
+
+
+class Admin_NewsletterControllerSendPreviouslyCollectedErrorActionTest
+  extends Admin_NewsletterControllerTestCase {
+
+  protected
+    $_command,
+    $_dispatch;
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->_command = $this->mock()
+                           ->whenCalled('exec')->answers(true)
+                           ->whenCalled('getOutput')->answers(['999'])
+                           ->whenCalled('getReturnVar')->answers(0);
+
+    Class_Batch_SendNewsletters::setCommand($this->_command);
+    Class_Newsletter_Dispatch::setTimeSource(new TimeSourceForTest('2016-07-21 11:21:38'));
+
+    Class_Newsletter_Dispatch::newFrom(Class_Newsletter::find(2))
+      ->setCollected(true)
+      ->setDispatchUsers([$this->fixture('Class_Newsletter_DispatchUser',
+                                         ['id' => '15', 'sent' => 1]),
+                          $this->fixture('Class_Newsletter_DispatchUser',
+                                         ['id' => '16', 'sent' => 0])])
+      ->setError(json_encode(['message' => 'An error occurred']))
+      ->setEndedOn('201-10-26 12:03:45')
+      ->assertSave();
+
+    $this->dispatch('/admin/newsletter/send/id/2', true);
+  }
+
+
+  public function tearDown() {
+    Class_Batch_SendNewsletters::setCommand(null);
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function shouldHaveOnlyOneDispatch() {
+    $this->assertEquals(1, Class_Newsletter::find(2)->numberOfDispatchs());
+  }
+
+
+  /** @test */
+  public function shouldHaveADispatchInProgress() {
+    $this->assertNotNull(Class_Newsletter::find(2)->getLastDispatchInProgress());
+  }
+}
+
+
+
+class Admin_NewsletterControllerSendPreviouslyCollectedErrorButAllSentActionTest
+  extends Admin_NewsletterControllerTestCase {
+
   protected
     $_command,
     $_dispatch;
@@ -523,8 +624,61 @@ class Admin_NewsletterControllerSendActionTest extends Admin_NewsletterControlle
     parent::setUp();
 
     $this->_command = $this->mock()
-                           ->whenCalled('exec')
-                           ->answers(true);
+                           ->whenCalled('exec')->answers(true)
+                           ->whenCalled('getOutput')->answers(['999'])
+                           ->whenCalled('getReturnVar')->answers(0);
+
+    Class_Batch_SendNewsletters::setCommand($this->_command);
+    Class_Newsletter_Dispatch::setTimeSource(new TimeSourceForTest('2016-07-21 11:21:38'));
+
+    Class_Newsletter_Dispatch::newFrom(Class_Newsletter::find(2))
+      ->setCollected(true)
+      ->setDispatchUsers([$this->fixture('Class_Newsletter_DispatchUser',
+                                         ['id' => '15', 'sent' => 1]),
+                          $this->fixture('Class_Newsletter_DispatchUser',
+                                         ['id' => '16', 'sent' => 1])])
+      ->setError(json_encode(['message' => 'An error occurred']))
+      ->setEndedOn('201-10-26 12:03:45')
+      ->assertSave();
+
+    $this->dispatch('/admin/newsletter/send/id/2', true);
+  }
+
+
+  public function tearDown() {
+    Class_Batch_SendNewsletters::setCommand(null);
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function shouldHaveOnlyOneDispatch() {
+    $this->assertEquals(2, Class_Newsletter::find(2)->numberOfDispatchs());
+  }
+
+
+  /** @test */
+  public function shouldHaveADispatchInProgress() {
+    $this->assertNotNull(Class_Newsletter::find(2)->getLastDispatchInProgress());
+  }
+}
+
+
+
+class Admin_NewsletterControllerSendActionTest
+  extends Admin_NewsletterControllerTestCase {
+
+  protected
+    $_command,
+    $_dispatch;
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->_command = $this->mock()
+                           ->whenCalled('exec')->answers(true)
+                           ->whenCalled('getOutput')->answers(['999'])
+                           ->whenCalled('getReturnVar')->answers(0);
 
     Class_Batch_SendNewsletters::setCommand($this->_command);
     Class_Newsletter_Dispatch::setTimeSource(new TimeSourceForTest('2016-07-21 11:21:38'));
@@ -555,7 +709,14 @@ class Admin_NewsletterControllerSendActionTest extends Admin_NewsletterControlle
   /** @test */
   public function commandShouldHaveBeenCalledWithDispatchId() {
     $this->assertContains(' "' . $this->_dispatch->getId() . '" ',
-                          $this->_command->getFirstAttributeForLastCallOn('exec'));
+                          $this->_command->getFirstAttributeForMethodCallAt('exec', 0));
+  }
+
+
+  /** @test */
+  public function commandShouldHaveBeenCalledWithBackgroudProcessId() {
+    $this->assertEquals('ps 999',
+                        $this->_command->getFirstAttributeForMethodCallAt('exec', 1));
   }
 
 
@@ -589,6 +750,54 @@ Lien pour se désinscrire de cette lettre d\'information : '. ROOT_URL . BASE_UR
 
 
 
+class Admin_NewsletterControllerSendActionWithCommandFailureTest extends Admin_NewsletterControllerTestCase {
+  protected
+    $_command,
+    $_dispatch;
+
+  public function setUp() {
+    parent::setUp();
+
+    $this->_command = $this->mock()
+                           ->whenCalled('exec')->answers(true)
+                           ->whenCalled('getOutput')->answers(['999'])
+                           ->whenCalled('getReturnVar')->answers(1);
+
+    Class_Batch_SendNewsletters::setCommand($this->_command);
+    Class_Newsletter_Dispatch::setTimeSource(new TimeSourceForTest('2016-07-21 11:21:38'));
+
+    $this->dispatch('/admin/newsletter/send/id/2', true);
+    $this->_dispatch = Class_Newsletter::find(2)->getDispatchs()[0];
+  }
+
+
+  public function tearDown() {
+    Class_Batch_SendNewsletters::setCommand(null);
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function shouldNotifyCommandError() {
+    $this->assertFlashMessengerContentContains('Envoi impossible : erreur à la création de la commande d\'envoi');
+  }
+
+
+  /** @test */
+  public function dispatchShouldHaveError() {
+    $this->assertContains('Unable to run /usr/bin/php -f',
+                          $this->_dispatch->getErrorMessage());
+  }
+
+
+  /** @test */
+  public function dispatchShouldBeEnded() {
+    $this->assertTrue($this->_dispatch->hasEndedOn());
+  }
+}
+
+
+
 
 class Admin_NewsletterControllerPreviewActionTest extends Admin_NewsletterControllerTestCase {
   public function setUp() {
@@ -1252,10 +1461,7 @@ class Admin_NewsletterControllerScriptTest extends Admin_NewsletterControllerTes
 
 abstract class Admin_NewsletterControllerSendProgressTestCase extends Admin_NewsletterControllerTestCase {
 
-  protected
-    $_storm_default_to_volatile = true,
-    $_json;
-
+  protected $_json;
 
   public function setUp() {
     parent::setUp();
@@ -1278,7 +1484,7 @@ class Admin_NewsletterControllerSendProgressWithWrongNewsletterIdTest extends Ad
 
 
   /** @test */
-  public function progressShouldReturnJsonUnknownNewsletter() {
+  public function statusShouldBeUnknownNewsletter() {
     $this->assertEquals('Newsletter inconnue', $this->_json->status);
   }
 
@@ -1290,81 +1496,99 @@ class Admin_NewsletterControllerSendProgressWithWrongNewsletterIdTest extends Ad
 
 
 
-class Admin_NewsletterControllerSendProgressErrorTest extends Admin_NewsletterControllerSendProgressTestCase {
-
-  /** @test */
-  public function progressShouldReturnError() {
-    $this->assertEquals('échec de l\'envoi', $this->_json->status);
-  }
-
+class Admin_NewsletterControllerSendProgressWithErrorAndNothingSentTest
+  extends Admin_NewsletterControllerSendProgressTestCase {
 
   protected function _dispatch() {
-    Class_Newsletter_Dispatch::newFrom(Class_Newsletter::find(1))->assertSave();
+    Class_Newsletter_Dispatch::newFrom(Class_Newsletter::find(1))
+      ->setError(json_encode(['message' => 'an error occured']))
+      ->setEndedOn('2016-10-27')
+      ->assertSave();
+
     parent::_dispatch();
   }
-}
-
-
 
-class Admin_NewsletterControllerSendProgressWithErrorTest extends Admin_NewsletterControllerSendProgressTestCase {
 
   /** @test */
-  public function progressShouldReturnError() {
-    $this->assertEquals('Aucune', $this->_json->status);
+  public function statusShouldContainsError() {
+    $this->assertContains('Erreur lors de l\'envoi', $this->_json->status);
   }
 
 
   /** @test */
-  public function newsletterDispatchTableShouldBeEmpty() {
-    $this->assertEmpty(Class_Newsletter_Dispatch::findAll());
+  public function statusShouldContainsNothingSent() {
+    $this->assertContains('aucun mail envoyé', $this->_json->status);
   }
+}
+
 
 
+class Admin_NewsletterControllerSendProgressWithErrorAnd1Of2SentTest
+  extends Admin_NewsletterControllerSendProgressTestCase {
+
   protected function _dispatch() {
-    $newsletter = Class_Newsletter::find(1);
-    $newsletter->setLastDistributionDate('')->assertSave();
-    Class_Newsletter_Dispatch::newFrom($newsletter)->assertSave();
+    $albator = $this->fixture('Class_Newsletter_DispatchUser',
+                              ['id' => '15', 'sent' => 1]);
+    $nausicaa = $this->fixture('Class_Newsletter_DispatchUser',
+                               ['id' => '16', 'sent' => 0]);
+
+    Class_Newsletter_Dispatch::newFrom(Class_Newsletter::find(1))
+      ->setCollected(true)
+      ->setDispatchUsers([$albator, $nausicaa])
+      ->setError(json_encode(['message' => 'an error occured']))
+      ->setEndedOn('2016-10-27')
+      ->assertSave();
+
     parent::_dispatch();
   }
-}
 
 
+  /** @test */
+  public function statusShouldContainsError() {
+    $this->assertContains('Erreur lors de l\'envoi', $this->_json->status);
+  }
+
 
-class Admin_NewsletterControllerSendProgressTest extends Admin_NewsletterControllerSendProgressTestCase {
   /** @test */
-  public function progressShouldReturnSending() {
-    $this->assertEquals('{"status":"en cours","done":0,"total":3}', $this->_response->getBody());
+  public function statusShouldContains1Of2Sent() {
+    $this->assertContains('envoyé à 1 sur 2', $this->_json->status);
   }
+}
 
 
-  protected function _dispatch() {
-    $dispatch = Class_Newsletter_Dispatch::newFrom(Class_Newsletter::find(1));
-    $dispatch->assertSave();
-    $dispatch->collectRecipients();
 
+class Admin_NewsletterControllerSendProgressNeverSentTest
+  extends Admin_NewsletterControllerSendProgressTestCase {
+
+  protected function _dispatch() {
+    Class_Newsletter::find(1)->setLastDistributionDate('')->assertSave();
     parent::_dispatch();
   }
+
+
+    /** @test */
+  public function progressShouldReturnNeverSent() {
+    $this->assertEquals('Aucune', $this->_json->status);
+  }
 }
 
 
 
-class Admin_NewsletterControllerNeverSendTest extends Admin_NewsletterControllerTestCase {
-  public function setUp() {
-    parent::setUp();
+class Admin_NewsletterControllerSendProgressWorkingTest
+  extends Admin_NewsletterControllerSendProgressTestCase {
 
-    $this->fixture('Class_Newsletter',
-                   ['id' => 3,
-                    'titre' => 'News of the Juny',
-                    'contenu' => 'A lot of new stuff',
-                    'last_distribution_date' => '']);
+  protected function _dispatch() {
+    $dispatch = Class_Newsletter_Dispatch::newFrom(Class_Newsletter::find(1));
+    $dispatch->assertSave();
+    $dispatch->collectRecipients();
 
-    $this->dispatch('admin/newsletter/send-progress/id/3', true);
+    parent::_dispatch();
   }
 
 
   /** @test */
-  public function progressShouldReturnNeverSendSending() {
-    $this->assertEquals('{"status":"Aucune"}', $this->_response->getBody());
+  public function progressShouldReturnSending() {
+    $this->assertEquals('{"status":"envoi en cours","done":0,"total":3}', $this->_response->getBody());
   }
 }
 
@@ -1574,3 +1798,34 @@ class Admin_NewsletterControllerEditSubcsriberReSubscriptionTest
   }
 
 }
+
+
+
+class Admin_NewsletterControllerShowStatusActionTest
+  extends Admin_NewsletterControllerTestCase {
+
+  /** @test */
+  public function withErrorShouldDisplayMessage() {
+    $this->fixture('Class_Newsletter_Dispatch', ['id' => 14])
+         ->setCollected(true)
+         ->setError(json_encode(['message' => 'An error occurred']))
+         ->setEndedOn('201-10-26 12:03:45')
+         ->assertSave();
+
+    $this->dispatch('/admin/newsletter/show-status/id/14', true);
+    $this->assertXPathContentContains('//p', 'An error occurred',
+                                      $this->_response->getBody());
+  }
+
+
+  /** @test */
+  public function withoutDispatchShouldBe404Error() {
+    try {
+      $this->dispatch('/admin/newsletter/show-status/id/14', true);
+    } catch(Zend_Controller_Action_Exception $e) {
+      $this->assertEquals(404, $e->getCode());
+      return;
+    }
+    $this->fail('should have 404 error');
+  }
+}
\ No newline at end of file
diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php
index 273b0d3d5b6d1f954efc32c78532e16abd552124..180c2838f5a89f680a7194a5998e790f54c16a0d 100644
--- a/tests/db/UpgradeDBTest.php
+++ b/tests/db/UpgradeDBTest.php
@@ -1134,3 +1134,19 @@ class UpgradeDB_312_Test extends UpgradeDBTestCase {
     $this->assertFieldType('ouvertures', 'multimedia', 'tinyint(1)');
   }
 }
+
+
+
+class UpgradeDB_313_Test extends UpgradeDBTestCase {
+  public function prepare() {
+    try {
+      $this->query('alter table newsletter_dispatch drop column error');
+    } catch(Exception $e) {}
+  }
+
+
+  /** @test */
+  public function dispatchShouldHaveErrorColumn() {
+    $this->assertFieldType('newsletter_dispatch', 'error', 'longtext');
+  }
+}
diff --git a/tests/scripts/SendNewsletterTest.php b/tests/scripts/SendNewsletterTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..6e7ec3e7f9a8358ad5a06f387cb124e08eba5ef7
--- /dev/null
+++ b/tests/scripts/SendNewsletterTest.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Copyright (c) 2012-2014, 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 Scripts_SendNewsletterTest extends PHPUnit_Framework_TestCase {
+  /** @test */
+  public function shouldSucceed() {
+    exec('cd ' . __DIR__ . '/../.. && php -f scripts/sendNewsletter.php "localhost" "localhost" "" "test"',
+         $output, $result);
+
+    $this->assertEquals(0, $result, implode("\n", $output));
+  }
+}
\ No newline at end of file