diff --git a/application/modules/admin/controllers/NewsletterController.php b/application/modules/admin/controllers/NewsletterController.php
index 2fcd2fbbdf8f533868b39497015664a658c8f1fd..cafcb1e1131c5a7ffcfc9d02c337b6bd9fb370d5 100644
--- a/application/modules/admin/controllers/NewsletterController.php
+++ b/application/modules/admin/controllers/NewsletterController.php
@@ -51,7 +51,6 @@ class Admin_NewsletterController extends Zend_Controller_Action {
 
 
 	public function indexAction() {
-		//évite de mocker findAll à chaque fois dans les tests
 		if (! $newsletter = Class_Newsletter::findAll())
 			$newsletter = [];
 		$this->view->newsletters = $newsletter;
@@ -143,40 +142,18 @@ class Admin_NewsletterController extends Zend_Controller_Action {
 																								 'search' => $this->_session_namespace['search'],
 																								 'page' => $this->_getParam('page')]);
 		$this->_forward('index');
-	}
-
-
-	public function sendNextAction() {
-		$this->_helper->viewRenderer->setNoRender();
-		$JSON = [];
-		if (!$newsletter = Class_Newsletter::find((int)$this->_request->getParam('id'))) {
-			echo json_encode($JSON[]=['error' => $this->view->_('La newsletter n\'existe pas')]);
-			return;
-		}
-		if (!$mail = (int) $this->_request->getParam('mail')) {
-			echo json_encode($JSON[]=['error' => $this->view->_('Pas d\'email à envoyer')]);
-			return;
-		}
-
-		echo json_encode($JSON[] = $newsletter->sendMail($mail));
 		return;
 	}
 
 
+
 	public function sendAction() {
+		$this->_helper->viewRenderer->setNoRender();
 		if (!$newsletter = Class_Newsletter::find((int)$this->_request->getParam('id')))
 			$this->_redirect('admin/newsletter');
-
-		try {
-			$newsletter->send();
-			$message = "Lettre envoyée:".$log;
-		} catch(Exception $e) {
-			$message = "Erreur à l'envoi de la lettre: ".$e->getMessage();
-			$this->getResponse()->setHttpResponseCode(500);
-		}
-		
-		$this->_helper->viewRenderer->setNoRender();
-		$this->_forward('index');
+		$newsletter->send();
+		$this->_redirect('admin/newsletter');
+		return;
 	}
 
 
diff --git a/application/modules/admin/views/scripts/newsletter/_newsletter_row.phtml b/application/modules/admin/views/scripts/newsletter/_newsletter_row.phtml
index b5080266a3d807278eeffbe92f19c74a44647528..44387446b4df61858fe7f1b280bd35d7e13075ca 100644
--- a/application/modules/admin/views/scripts/newsletter/_newsletter_row.phtml
+++ b/application/modules/admin/views/scripts/newsletter/_newsletter_row.phtml
@@ -1,25 +1,19 @@
 <tr class="<?php echo $this->item_class ?>">
 	<td><?php echo htmlentities($this->newsletter->getTitre(), ENT_QUOTES, 'UTF-8'); ?></td>
-	<td><?php 
-			if (null == $last_date = $this->newsletter->getDistributionProgress())
-				echo 'Aucune';
-			else
-				echo $last_date;
-		?></td>
-
-		<?php 
-			 foreach(['edit' => $this->boutonIco("type=edit"),
-							 'delete' => $this->boutonIco("type=del"), 
-							 'preview' => $this->boutonIco("type=show"), 
-							 'edit-subscribers' => $this->boutonIco("picto=picto/abonnes.gif", "bulle=Membres").$this->newsletter->getNumberOfUsers(), 
-							 'sendtest' => $this->boutonIco("type=test"), 
-							 'send' => $this->boutonIco("type=mail")]
-						as $action => $view) {
-				echo "<td rel='$action'>";
-				echo $this->tagAnchor($this->url(['action' => $action,
-																					'id' => $this->newsletter->getId()]),
-															$view);
-				echo '</td>';
-		}
-	 ?>
+	<td><?php echo $this->newsletter->getDistributionProgress();?></td>
+	<?php 
+	foreach(['edit' => $this->boutonIco("type=edit"),
+					 'delete' => $this->boutonIco("type=del"), 
+					 'preview' => $this->boutonIco("type=show"), 
+					 'edit-subscribers' => $this->boutonIco("picto=picto/abonnes.gif", "bulle=Membres").$this->newsletter->getNumberOfUsers(), 
+					 'sendtest' => $this->boutonIco("type=test"), 
+					 'send' => $this->boutonIco("type=mail")]
+					as $action => $view) {
+		echo "<td rel='$action'>";
+		echo $this->tagAnchor($this->url(['action' => $action,
+																			'id' => $this->newsletter->getId()]),
+													$view);
+		echo '</td>';
+	}
+	?>
 </tr>
diff --git a/application/modules/admin/views/scripts/newsletter/send.phtml b/application/modules/admin/views/scripts/newsletter/send.phtml
index 77eec70b08b343b75eeee1ab20f5fe494e3ce4cb..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/application/modules/admin/views/scripts/newsletter/send.phtml
+++ b/application/modules/admin/views/scripts/newsletter/send.phtml
@@ -1,11 +0,0 @@
-<?php
-Class_ScriptLoader::getInstance()
-->addScript(URL_JAVA . 'progress_bar/progress_bar.js')
-->addJQueryReady('$(".sending_mails").progress_bar()');
-echo $this->message;
-$html = $this->tag('div',
-									 '',
-									 ['class' => 'sending_mails',
-										'data-url' => $this->first_mail]);
-echo $html;
-?>
diff --git a/library/Class/Batch.php b/library/Class/Batch.php
index b2e2d0469dc6458587f415f002dc09b57832a886..ee0cadc207ca5050369bba2e0482cb3fc4185373 100644
--- a/library/Class/Batch.php
+++ b/library/Class/Batch.php
@@ -27,8 +27,7 @@ class Class_BatchLoader extends Storm_Model_Loader{
 						'CORRECTION_PANIERS' => new Class_Batch_PanierNotice(),
 						'CART_REALLOCATION' => new Class_Batch_PanierUser(),
 						'COMMENT_REALLOCATION'=> new Class_Batch_AvisNotice(),
-						'INDEX_RESSOURCES_NUMERIQUES' => new Class_Batch_IndexRessourcesNumeriques(),
-						'SEND_NEWSLETTERS' => new Class_Batch_SendNewsletters()
+						'INDEX_RESSOURCES_NUMERIQUES' => new Class_Batch_IndexRessourcesNumeriques()
 			]);
 	}
 
diff --git a/library/Class/Batch/SendNewsletters.php b/library/Class/Batch/SendNewsletters.php
index 57ee02edb2492b3d809b23153f47399891b38470..7aa600738660b93da2b57d6cf65d6772b18866f6 100644
--- a/library/Class/Batch/SendNewsletters.php
+++ b/library/Class/Batch/SendNewsletters.php
@@ -20,24 +20,26 @@
  */
 
 
+
 class Class_Batch_SendNewsletters extends Class_Batch_Abstract {
-	protected $log="";
 	public function __construct($newsletter) {
 		$this->_newsletters = $newsletter;
+		return $this;
 	}
 
+
 	public function getLabel() {
 		return $this->_('Envoie des newsletters');
 	}
 
-	public function getLog() {
-		return $this->log;
-	}
+
 	public function run() {
 		Class_NewsletterSubscription::resetSendFlagForNewsletter($this->_newsletters->getId());
-		$this->log = $this->_newsletters->generateMails(200);
 
+		exec("/usr/bin/php -f ".realpath(dirname(__FILE__))."/../../../scripts/sendNewsletter.php "
+					 .$this->_newsletters->getId().' "'.$_SERVER['HTTP_HOST'].'"'." > /dev/null &"); 
 		return $this;
 	}
 }
+
 ?>
\ No newline at end of file
diff --git a/library/Class/Newsletter.php b/library/Class/Newsletter.php
index 49ae719cab2ebf5e9a26eb59f36aa6b686f4d86e..f2ccffffbdf0dd81e48772d47427a9335cf1f2f6 100644
--- a/library/Class/Newsletter.php
+++ b/library/Class/Newsletter.php
@@ -54,12 +54,14 @@
  */
 
 class Class_Newsletter extends Storm_Model_Abstract {
+	use Trait_TimeSource, Trait_Translator;
+	
 	protected $_table_name = 'newsletters';
 	protected $_has_many = ['subscriptions' => ['model' => 'Class_NewsletterSubscription',
 																							'role' => 'newsletter',
 																							'dependents' => 'delete']];
 	protected $_notices_finder;
-	protected $_recipent_size = 200;
+	protected $_recipent_size = 20;
 
 
 	public function getSubscriptionsPage($page=0, $items_by_page=20) {
@@ -68,36 +70,10 @@ class Class_Newsletter extends Storm_Model_Abstract {
 	}
 
 	
-	protected function getNumberOfMailsToSend() {
-		$count = Class_NewsletterSubscription::countAvailableUserForNewsletter($this->getId());
-		if(0 === $count)
-			return 0;
-		return $count % $this->_recipient_size;
-	}
-
-/*
-	public function sendMail($mail_iterator) {
-		$cound_mails = $this->getNumberOfMailsToSend();
-		$next_mail = ($mail_iterator < $cound_mails) ? $mail_iterator + 1 : 0;
-		$mail = $this->generateMail($mail_iterator);
-		$mail->send();
-
-		$this
-			->setLastDistributionDate(strftime("%Y-%m-%d %H:%M:%S"))
-			->save();
-		
-		$JSON = [];
-		return json_encode($JSON[] = ['success' => $this->_('Mail '.$mail_iterator.'/'.$cound_mails.' envoyé avec succès'),
-																	'next-send' => $next_mail]);
-	}
-*/
-
 	public function send() {
-		$batch_newsletters = new Class_Batch_SendNewsletters($this);
-		$batch_newsletters->run();
-
-		$this
-			->setLastDistributionDate(strftime("%Y-%m-%d %H:%M:%S"))
+		(new Class_Batch_SendNewsletters($this))->run();
+		return $this
+			->setLastDistributionDate($this->getCurrentTime())
 			->save();
 	}
 
@@ -109,6 +85,7 @@ class Class_Newsletter extends Storm_Model_Abstract {
 	}
 
 
+
 	public function getExpediteur() {
 		if ($this->isAttributeEmpty('expediteur'))
 			return $this->_getMailPortail();
@@ -145,12 +122,11 @@ class Class_Newsletter extends Storm_Model_Abstract {
 				? $mail->addBcc($user_mail)
 				: '';
 		}
-			
 		return $mail;
-		
 	}
 
 	
+
 	public function getNumberOfUsers() {
 		return sprintf('%05d', Class_NewsletterSubscription::countAvailableUserForNewsletter($this->getId()));
 	}
@@ -158,8 +134,8 @@ class Class_Newsletter extends Storm_Model_Abstract {
 
 
 	public function generateMails($recipient_size) {
-
-		while (strlen($receivers_list = $this->getReceiversList($receivers,$recipient_size))>0) {
+		while (0 < count(array_filter( $receivers = $this->getReceivers($recipient_size)))) {
+			xdebug_break();
 			Class_NewsletterSubscription::clearCache();
 			Class_Users::clearCache();
 			Storm_Model_Loader::resetCache();
@@ -169,23 +145,18 @@ class Class_Newsletter extends Storm_Model_Abstract {
 			$mail = $this->_newMail();
 			$mail->addTo($this->getExpediteur());
 
-
-			$mail->addBcc($receivers_list);
+			foreach($receivers as $receiver)
+				$mail->addBcc($receiver->getMail());
 			$mail->send();
-			
-			Class_NewsletterSubscription::updateSendFlagForReceivers($this->getId(),$receivers);
+
+			Class_NewsletterSubscription::updateSendFlagForReceivers($this->getId(),$this->getReceivers($recipient_size));
 
 		}
 	}
 
 
-	protected function getReceiversList($receivers,$recipient_size) {
-		$receivers = Class_Users::getNewslettersReceivers($this->getId(), $recipient_size);
-		$emails = [];
-		foreach ($receivers as $user) {
-			$emails[]= $user->getMail();
-		}
-		return implode(',',$emails);
+	protected function getReceivers($recipient_size) {
+			return Class_Users::getNewslettersReceivers($this->getId(), $recipient_size);
 	}
 
 
@@ -205,7 +176,7 @@ class Class_Newsletter extends Storm_Model_Abstract {
 													'only_img' => false,
 													'aleatoire' => 0,
 													'tri' => 1);
-		return Class_Notice::getLoader()->getNoticesFromPreferences($preferences);
+		return Class_Notice::getNoticesFromPreferences($preferences);
 	}
 
 
@@ -294,10 +265,13 @@ class Class_Newsletter extends Storm_Model_Abstract {
 
 	public function getDistributionProgress() {
 		$progress = Class_NewsletterSubscription::countDistributionProgress($this->getId());
+		$last_date = $this->getLastDistributionDate();
+		if(!$last_date)
+			return $this->_('Aucune');
 		if ($progress <1)
-			return date('d/m/Y H:i', strtotime( $this->getLastDistributionDate()));
+			return date('d/m/Y H:i', strtotime($last_date)); 
 		$total=Class_NewsletterSubscription::countTotalDistribution($this->getId());
-		return $progress."/".$total;
+		return 'distribution en cours...'.($total-$progress)."/".$total;
 		
 	}
 }
diff --git a/library/Class/NewsletterSubscription.php b/library/Class/NewsletterSubscription.php
index 327b520932f88aac42c8d9c5a48a23b7f2fff9e2..a181f3d6fcf3492776962ec1bd8070731978cb21 100644
--- a/library/Class/NewsletterSubscription.php
+++ b/library/Class/NewsletterSubscription.php
@@ -33,7 +33,7 @@ class NewsletterSubscriptionLoader extends Storm_Model_Loader {
 
 	public function countDistributionProgress($id_newsletter) {
 		return Class_NewsletterSubscription::countBy(['newsletter_id' => $id_newsletter,
-																									'send' => 0]);
+																									'send' => 'false']);
 	}
 
 	public function countTotalDistribution($id_newsletter) {
@@ -58,7 +58,10 @@ class NewsletterSubscriptionLoader extends Storm_Model_Loader {
 	}
 
 	public function resetSendFlagForNewsletter($id_newsletter) {
-		return sqlExecute("update newsletters_users set send=false where newsletter_id=".$id_newsletter);
+		if(0===count(array_filter(Class_NewsletterSubscription::findAllBy(['newsletter_id' => $id_newsletter,
+																																			 'send' => false]))))
+			return sqlExecute("update newsletters_users set send=false where newsletter_id=".$id_newsletter);
+		return '';
 	}
 
 	public function updateSendFlagForReceivers($id_newsletter,$receivers) {
diff --git a/scripts/sendNewsletter.php b/scripts/sendNewsletter.php
new file mode 100644
index 0000000000000000000000000000000000000000..9e7e03fadf63bebca9b73c8c0d4eb6d31dcb124e
--- /dev/null
+++ b/scripts/sendNewsletter.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * AFI-OPAC 2.0 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).
+ *
+ * AFI-OPAC 2.0 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 AFI-OPAC 2.0; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ */
+define("BASE_URL", "/");
+
+
+
+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'
+	);
+$_SERVER['HTTP_HOST']=$argv[2];
+include_once "local.php";
+include_once "fonctions/fonctions.php";
+require_once "Zend/Loader.php";
+require_once "startup.php";
+
+setupOpac();
+
+$news=(Class_Newsletter::find($argv[1]));
+$news->generateMails(20);
+
+?>
diff --git a/tests/application/modules/admin/controllers/NewsletterControllerTest.php b/tests/application/modules/admin/controllers/NewsletterControllerTest.php
index f10d4c6481bc0cc567c8d4edbd457472a2edd75e..c1eae8c2d04cdf89ab5237e59600e3de6798b212 100644
--- a/tests/application/modules/admin/controllers/NewsletterControllerTest.php
+++ b/tests/application/modules/admin/controllers/NewsletterControllerTest.php
@@ -27,7 +27,11 @@ abstract class Admin_NewsletterControllerTestCase extends Admin_AbstractControll
 
 		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_PanierNotice')
 			->whenCalled('findAllBelongsToAdmin')
-			->answers(array());
+			->answers([]);
+
+		Storm_Test_ObjectWrapper::mock()
+			->whenCalled('run')
+			->answers(true);
 	}
 }
 
@@ -38,28 +42,45 @@ class Admin_NewsletterControllerIndexActionTest extends Admin_NewsletterControll
 	public function setUp() {
 		parent::setUp();
 
-		$fixtures = array(
-											array('id' => 1,
-														'titre' => 'Nouveautés classique',
-														'contenu' => 'Notre sélection du mois',
-														'last_distribution_date' => '2005-03-27 12:30:00'),
+		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_NewsletterSubscription')
+			->whenCalled('countAvailableUserForNewsletter')
+			->with(1)
+			->answers(1)
 
-											array('id' => 2,
-														'titre' => 'Animations',
-														'contenu' => 'Pour les jeunes',
-														'last_distribution_date' => null));
+			->whenCalled('countAvailableUserForNewsletter')
+			->with(2)
+			->answers(1)
+			
+			->whenCalled('countDistributionProgress')
+			->with(1)
+			->answers(0)
 
-		$mock_results = new Zend_Db_Table_Rowset(array('data' => $fixtures));
-		$tbl_newsletters = $this->getMock('MockTableNewsletters',
-																			array('fetchAll'));
+			->whenCalled('countDistributionProgress')
+			->with(2)
+			->answers(0)
+			
+			->whenCalled('countTotalDistribution')
+			->with(1)
+			->answers(1)
 
-		$tbl_newsletters
-			->expects($this->once())
-			->method('fetchAll')
-			->will($this->returnValue($mock_results));
+			->whenCalled('countTotalDistribution')
+			->with(2)
+			->answers(1)
 
-		Class_Newsletter::getLoader()->setTable($tbl_newsletters);
-		$this->dispatch('/admin/newsletter');
+			->beStrict();
+
+		$this->fixture('Class_Newsletter',
+									 ['id' => 1,
+										'titre' => 'Nouveautés classique',
+										'contenu' => 'Notre sélection du mois',
+										'last_distribution_date' => '2005-03-27 12:30:00']);
+		$this->fixture('Class_Newsletter',
+									 ['id' => 2,
+										'titre' => 'Animations',
+										'contenu' => 'Pour les jeunes',
+										'last_distribution_date' => null]);
+
+		$this->dispatch('/admin/newsletter', true);
 	}
 
 
@@ -73,12 +94,12 @@ class Admin_NewsletterControllerIndexActionTest extends Admin_NewsletterControll
 	}
 
 	public function testLastDistributionDateForNouveautesClassique() {
-		$this->assertXPathContentContains("//tbody//tr[1]//td", '27/03/2005 12:30');
+		$this->assertXPathContentContains("//tbody//tr[1]//td", '27/03/2005 12:30', $this->_response->getBody());
 	}
 
 	/** @test */
 	public function numberOfSubscriberShouldBeTwo() {
-		$this->assertXPathContentContains("//tbody//tr[1]//td//a[@href='/admin/newsletter/edit-subscribers/id/1']", '00000');
+		$this->assertXPathContentContains("//tbody//tr[1]//td//a[@href='/admin/newsletter/edit-subscribers/id/1']", '00001');
 	}
 
 	public function testEditNouveautesClassiqueLink() {
@@ -110,7 +131,7 @@ class Admin_NewsletterControllerIndexActionTest extends Admin_NewsletterControll
 	}
 
 	public function testLastDistributionDateForAnimationsIsNone() {
-		$this->assertXPathContentContains("//tbody//tr[2]//td", 'Aucune');
+		$this->assertXPathContentContains("//tbody//tr[2]//td", 'Aucune', $this->_response->getBody());
 	}
 
 	public function testListAnimationsEditLink() {
@@ -419,35 +440,19 @@ class Admin_NewsletterControllerSendActionTest extends Admin_NewsletterControlle
 			->whenCalled('find')
 			->with(4)
 			->answers($this->newsletter);
-	}
 
-	public function testSendCalled() {
 		$this->newsletter
 			->whenCalled('send')
 			->answers(true)
 			->beStrict();
 
 		$this->dispatch('/admin/newsletter/send/id/4');
-
-		$this->assertResponseCode(200);
-		$this->assertEquals($this->_response->getBody(), 
-												'Lettre envoyée');
 	}
 
-
-	public function testSendRaisesException() {
-		$this->newsletter
-			->whenCalled('send')
-			->willDo(
-				function() {
-					throw new Zend_Mail_Protocol_Exception('Connection timed out');
-				});
-
-		$this->dispatch('/admin/newsletter/send/id/4');
-
-		$this->assertResponseCode(500);
-		$this->assertEquals($this->_response->getBody(), 
-												"Erreur à l'envoi de la lettre: Connection timed out");
+	
+	/** @test */
+	public function shouldRedirectToNewsletterIndex() {
+		$this->assertRedirectTo('/admin/newsletter');
 	}
 }
 
@@ -872,66 +877,5 @@ class Admin_NewsletterControllerMailingPaginationTest extends Admin_AbstractCont
 		return $users;
 	}
 
-
-	/** @test */
-	public function progressBarShouldBeDisplay() {
-		$this->assertXPath('//div[@class="subview"]//div[@class="sending_mails"][@data-url="/admin/newslettger/send-next/id/1/mail/1"]');
-	}
-
-
-	/** @test */
-	public function progessBarPluginShouldBeLoaded() {
-		$this->assertXPath('//script[contains(@src, "java/progress_bar/progress_bar.js")]');
-	}
-
-
-	/** @test */
-	public function progressBarShouldBeCall() {
-		$this->assertXPathContentContains('//script', '.progress_bar()');
-	}
-}
-
-
-
-
-class Admin_NewsletterControllerSendNextMailTest extends Admin_AbstractControllerTestCase {
-	public function setup() {
-		parent::setup();
-		$this->fixture('Class_Newsletter',
-									 ['id' => 1,
-										'titre' => 'Actu',
-										'contenu' => 'Wait a minute']);
-
-		$this->mock_transport = new MockMailTransport();
-		Zend_Mail::setDefaultTransport($this->mock_transport);
-	}
-	
-	
-	/** @test */
-	public function shouldReturnExpectedError() {
-		$this->dispatch('/admin/newsletter/send-next/id/9889/mail/1', true);
-				
-		$expected_json = ['error' => 'La newsletter n\'existe pas'];
-		$this->assertEquals(json_encode($expected_json), $this->_response->getBody());
-	}
-
-
-		/** @test */
-	public function shouldReturnExpectedMessage() {
-		$this->dispatch('/admin/newsletter/send-next/id/1/mail/', true);
-				
-		$expected_json = ['error' => 'Pas d\'email à envoyer'];
-		$this->assertEquals(json_encode($expected_json), $this->_response->getBody());
-	}
-
-
-		/** @test */
-	public function shouldReturnExpectedResult() {
-		$this->dispatch('/admin/newsletter/send-next/id/1/mail/1', true);
-				
-		$expected_json = ['succcess' => 'mail 1/10 envoyé',
-											'send-next' => '/admin/newsletter/send-next/id/1/mail/2'];
-		$this->assertEquals(json_encode($expected_json), $this->_response->getBody());
-	}
 }
 ?>
\ No newline at end of file
diff --git a/tests/library/Class/BatchTest.php b/tests/library/Class/BatchTest.php
index 88a934b7fe7cf5713b55118566724ff8a5b51fe9..19d954ac0e4ef05d12b0aae028b7b1dc2c9dd499 100644
--- a/tests/library/Class/BatchTest.php
+++ b/tests/library/Class/BatchTest.php
@@ -146,32 +146,4 @@ class BatchIndexRessourcesNumeriquesTest extends Storm_Test_ModelTestCase {
 		$this->assertTrue($this->cache->methodHasBeenCalled('clean'));
 	}
 }
-
-
-class BatchSendNewsletterByMailTest extends Storm_Test_ModelTestCase {
-	public function setup() {
-		parent::setup();
-		$tom = $this->fixture('Class_Users',
-													['id' => 1,
-													 'login' => 'tom',
-													 'password'=>'pwd']);
-
-		$newsletter = $this->fixture('Class_Newsletter',
-																 ['id' => 1,
-																	'titre'=>'News',
-																	'contenu' => 'WAT?']);
-		$subscription = $this->fixture('Class_NewsletterSubscription',
-																	 ['id' => 1,
-																		'newsletter_id' => 1,
-																		'user_id' => 1,
-																		'send'=>false]);
-		(new Class_Batch_SendNewsletters)->run();
-	}
-
-	
-	/** @test */
-	public function tomShouldHaveRecieveNews() {
-		$this->assertEquals(true, Class_NewsletterSubscription::find(1)->getSend());
-	}
-}
 ?>
\ No newline at end of file
diff --git a/tests/library/Class/NewsletterMailingTest.php b/tests/library/Class/NewsletterMailingTest.php
index bd019a51550490e11546e51086c89446af3b9eb6..1f20f95df5a85d93aaf8abe3987b6f75baa8e15a 100644
--- a/tests/library/Class/NewsletterMailingTest.php
+++ b/tests/library/Class/NewsletterMailingTest.php
@@ -22,14 +22,38 @@ require_once 'Class/Newsletter.php';
 require_once 'ModelTestCase.php';
 
 
-class NewsletterMailingAnimationsTestSendMail extends ModelTestCase {
-	public function setUp() {
-		// Les mails envoyés reprennent l'adresse de la configuration du profil Portail
+
+abstract class NewsletterMailingTestCase extends ModelTestCase {
+	public function setup() {
+		parent::setup();
+		
+		Class_PanierNotice::beVolatile();
+		Class_Catalogue::beVolatile();
+
+		$time_source = new TimeSourceForTest('2014-05-23 14:30:00');
+		Class_Newsletter::setTimeSource($time_source);
+
 		$profil_portail = $this->fixture('Class_Profil',
 																		 ['id' => 1,
-																			'mail_site' => 'flo@astrolabe.fr']);
+																			'mail_site' => 'flo@afi-sa.fr']);
+
+		$this->millenium = $this->fixture('Class_Notice',
+																			['id' => 345,
+																			 'titre_principal' => "Les hommes qui n'aimaient pas les femmes",
+																			 'resume' => 'Polard du nord',
+																			 'auteur_principal' => 'Stieg Larsson',
+																			 'annee' => 2005,
+																			 'url_vignette' => 'http://afi-sa.fr/millenium.png']);
+		
+		$this->potter = $this->fixture('Class_Notice',
+																	 ['id' => 987,
+																		'titre_principal' => "Harry Potter à l'école des sorciers",
+																		'resume' => "L'histoire d'un sorcier...",
+																		'auteur_principal' => 'J.K. Rowling',
+																		'annee' => 1998,
+																		'url_vignette' => 'http://afi-sa.fr/potter.gif']);
 
-		//A l'envoi du mail, la newsletter doit être sauvegardée (pour mémoriser au moins la date d'envoi)
+		$this->notices = [$this->millenium, $this->potter];
 
 		$this->rdubois = $this->fixture('Class_Users',
 																		['id' => 1,
@@ -65,35 +89,99 @@ class NewsletterMailingAnimationsTestSendMail extends ModelTestCase {
 																			 ['id' => 1,
 																				'titre' => 'Animations du mois',
 																				'contenu' => 'Découverte des cuisines du monde',
-																				'id_panier' => 0,
-																				'id_catalogue' => 0,
+																				'id_panier' => 23,
+																				'id_catalogue' => null,
+																				'nb_notices' => 2,
+																				'last_distribution_date' => '2014-05-23 14:30:00',
 																				'users' => [$this->rdubois,
 																										$this->mduchamp,
 																										$this->zork,
 																										$this->zorkglub]]);
 
+
+		$this->user_model=Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Users');
+		$this->user_model
+			->whenCalled('clearCache')
+			->answers(true)
+
+			->whenCalled('getNewslettersReceivers')
+			->with(1, 20)
+			->willDo(function() { return $this->getReceivers();})
+			->beStrict();
+
+
 		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_NewsletterSubscription')
-			->whenCalled('countAvailableUserForNewsletter')
-			->with(1)
-			->answers(1)
 			->whenCalled('clearCache')
+			->answers(true)			
+
+			->whenCalled('countDistributionProgress')
+			->with(1)
+			->answers(0)
+
+			->whenCalled('updateSendFlagForReceivers')
+			->with(1,[$this->rdubois,
+								$this->mduchamp,
+								$this->zork,
+								$this->zorkglub])
+			->answers(false)
+
+			->whenCalled('updateSendFlagForReceivers')
+			->with(1,[])
+			->answers(false)
+
+			->whenCalled('resetSendFlagForNewsletter')
+			->with(1)
 			->answers(true)
-			->whenCalled('getAvailableUsersForNewsletter')
-			->with(1, 0, 200)
-			->answers([$this->rdubois,
-								 $this->mduchamp])
+			
 			->beStrict();
 
+		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Notice')
+			->whenCalled('getNoticesFromPreferences')
+			->willDo(function() {return $this->notices;});
+
+		Storm_Test_ObjectWrapper::mock()
+			->whenCalled('run')
+			->answers(true);
 
 		$this->mock_transport = new MockMailTransport();
 		Zend_Mail::setDefaultTransport($this->mock_transport);
-		$this->animations->send();
+		
+		$this->animations->generateMails(20);
+
+		$this->mail = $this->mock_transport->sent_mail;
+	}
+
+	
+	protected function getReceivers() {
+		if ($this->user_model->methodCallCount('getNewslettersReceivers')>1)
+			 return [];
+		return [$this->rdubois,
+						$this->mduchamp,
+						$this->zork,
+						$this->zorkglub];
+	}
+}
+
+
+
+
+class NewsletterMailingAnimationsTestSendMail extends NewsletterMailingTestCase {
+	public function setUp() {
+		parent::setup();
+		$this->animations->generateMails(20);
 		$this->mail = $this->mock_transport->sent_mail;
 	}
+	
+
+	/** @test */
+	public function floShouldBeExpeditor() {
+		$this->assertEquals('flo@afi-sa.fr', $this->animations->getExpediteur());
+	}
+
 
 	public function testToArrayContainsAdminPortailAsExpediteur() {
-		$this->assertContains('flo@astrolabe.fr',
-													$this->animations->toArray());
+		$this->assertContains('flo@afi-sa.fr',
+													$this->mail->getFrom());
 	}
 
 	public function testSubjectIsAnimationsDuMois() {
@@ -107,7 +195,7 @@ class NewsletterMailingAnimationsTestSendMail extends ModelTestCase {
 
 	public function testBccIncludesRduboisAtFreeDotFr() {
 		$this->assertContains('rdubois@afi-sa.fr',
-													$this->mail->getRecipients());
+													implode(',',$this->mail->getRecipients()));
 	}
 
 	public function testBccIncludesMduchampAtHotmailDotCom() {
@@ -123,7 +211,7 @@ class NewsletterMailingAnimationsTestSendMail extends ModelTestCase {
 
 
 	public function testToIsAdminPortail() {
-		$this->assertContains('flo@astrolabe.fr',
+		$this->assertContains('flo@afi-sa.fr',
 													$this->mail->getRecipients());
 	}
 
@@ -134,169 +222,23 @@ class NewsletterMailingAnimationsTestSendMail extends ModelTestCase {
 
 
 	public function testSenderIsAdminPortail() {
-		$this->assertEquals('flo@astrolabe.fr', $this->mail->getFrom());
+		$this->assertEquals('flo@afi-sa.fr', $this->mail->getFrom());
 	}
 
-	public function testGenerateMailIsSameAsSentMail() {
-		$generateds = $this->animations->generateMails();
-		$generateds[0]->setDate($this->mail->getDate());
-		$this->assertEquals($this->mail, $generateds[0]);
-	}
 
 	public function testNewsletterLastDistributionDateIsNow() {
-		$this->assertEquals(strftime('%Y-%m-%d'),
-												strftime('%Y-%m-%d',
-																 strtotime($this->animations->getLastDistributionDate()) ));
-	}
-
-
-	public function testSettingExpediteurUseIt() {
-		$this->animations->setExpediteur('cyberlab@astrolabe.fr');
-		$mail = $this->animations->generateMails()[0];
-
-		$this->assertEquals('cyberlab@astrolabe.fr',
-												$mail->getFrom());
-
-		$this->assertContains('cyberlab@astrolabe.fr',
-													$mail->getRecipients());
-
-		$this->assertNotContains('flo@astrolabe.fr',
-														 $mail->getRecipients());
-
-		$this->assertEquals(3, count($this->mail->getRecipients()));
-	}
-
-
-	public function testSendToOnlySendToGivenRecipient() {
-		$this->animations->sendTo('marcel@free.fr');
-		$mail = $this->mock_transport->sent_mail;
-
-		$this->assertEquals(1, count($mail->getRecipients()));
-
-		$this->assertContains('marcel@free.fr',
-													$mail->getRecipients());
+		$this->assertEquals('23/05/2014 14:30', $this->animations->getDistributionProgress());
 	}
 }
 
 
 
 
-class NewsletterMailingConcertsTestMailRecipients extends ModelTestCase {
+class NewsletterMailingConcertsTestPanier extends NewsletterMailingTestCase {
 	public function setUp() {
-		
-		$jpasse = $this->fixture('Class_Users',
-														 ['id' => 1,
-															'prenom' => 'Jean',
-															'nom' => 'Passe',
-															'mail' => 'jpasse@afi-sa.fr',
-															'login' => 'jpa',
-															'password' => 'pwd']);
-
-		$user_without_mail = $this->fixture('Class_Users',
-																				['id' => 2,
-																				 'prenom' => 'Mata',
-																				 'nom' => 'Hari',
-																				 'mail' => null,
-																				 'login' => 'mh',
-																				 'password' => 'pwd']);
-
-		$concerts = $this->fixture('Class_Newsletter',
-															 ['id' => 1,
-																'titre' => 'Concerts',
-																'contenu' => 'Marcus Miller<br />au Jazz Festival',
-																'id_catalogue' => null,
-																'id_panier' => null,
-																'users' => [$jpasse,
-																						$user_without_mail]]);
-
-		Storm_Test_ObjectWrapper::onLoaderofModel('Class_NewsletterSubscription')
-			->whenCalled('clearCache')
-			->answers(true)
-			->whenCalled('countAvailableUserForNewsletter')
-			->with(1)
-			->answers(1)
-			->whenCalled('getAvailableUsersForNewsletter')
-			->with(1, 0, 200)
-			->answers([$jpasse])
-			->beStrict();
-		
-		Class_Profil::getPortail()->setMailSite('');
-
-		$this->mail = $concerts->generateMails()[0];
-	}
-
-	public function testSubjectIsConcerts() {
-		$this->assertEquals('Concerts', $this->mail->getSubject());
-	}
-
-
-	public function testBodyTextIsMarcusAuJazz() {
-		$this->assertContains("Marcus Miller\nau Jazz Festival",
-													quoted_printable_decode($this->mail->getBodyText()->getContent()));
-	}
-
-
-	public function testBodyHTMLBreaksLines() {
-		$this->assertContains("Marcus Miller<br />au Jazz Festival",
-													quoted_printable_decode($this->mail->getBodyHTML()->getContent()));
-	}
-
-
-	public function testBccIncludesJean() {
-		$this->assertContains('jpasse@afi-sa.fr',
-													$this->mail->getRecipients());
-	}
-
-	public function testRecipientsSizeIsOne() {
-		$this->assertEquals(1, count($this->mail->getRecipients()));
-	}
-}
-
-
-
-
-class NewsletterMailingConcertsTestPanier extends ModelTestCase {
-	public function setUp() {
-		$this->millenium = $this->fixture('Class_Notice',
-																			['id' => 345,
-																			 'titre_principal' => "Les hommes qui n'aimaient pas les femmes",
-																			 'resume' => 'Polard du nord',
-																			 'auteur_principal' => 'Stieg Larsson',
-																			 'annee' => 2005,
-																			 'url_vignette' => 'http://afi-sa.fr/millenium.png']);
-		
-		$this->potter = $this->fixture('Class_Notice',
-																	 ['id' => 987,
-																		'titre_principal' => "Harry Potter à l'école des sorciers",
-																		'resume' => "L'histoire d'un sorcier...",
-																		'auteur_principal' => 'J.K. Rowling',
-																		'annee' => 1998,
-																		'url_vignette' => 'http://afi-sa.fr/potter.gif']);
-		
-			$selection = $this->fixture('Class_Newsletter',
-																	['id' => 1,
-																	 'titre' => 'Selection',
-																	 'contenu' => 'Notices of the month',
-																	 'id_panier' => 23,
-																	 'id_catalogue' => null,
-																	 'nb_notices' => 2]);
-
-			Storm_Test_ObjectWrapper::onLoaderOfModel('Class_NewsletterSubscription')
-				->whenCalled('countAvailableUserForNewsletter')
-				->with(1)
-				->answers(1)
-				->whenCalled('clearCache')
-				->answers(true)
-				->whenCalled('getAvailableUsersForNewsletter')
-				->with(1, 0, 200)
-				->answers([$this->fixture('Class_Users',
-													['id' => 15,
-													 'mail' => 'gloas@afi-sa.fr',
-													 'login'=>'gloas',
-													 'password'=>'pwd'])])
-				->beStrict();
-
-			$this->mail = $selection->generateMails()[0];
+		parent::setup();
+		$this->animations->generateMails(20);
+		$this->mail = $this->mock_transport->sent_mail;
 	}