diff --git a/core/Test/NotificationScheduleControllerTest.php b/core/Test/NotificationScheduleControllerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..296380376b7d67e20aaa4c5a8060ee30a11de6bb
--- /dev/null
+++ b/core/Test/NotificationScheduleControllerTest.php
@@ -0,0 +1,219 @@
+<?php
+/**
+* Copyright Maarch since 2008 under licence GPLv3.
+* See LICENCE.txt file at the root folder for more details.
+* This file is part of Maarch software.
+
+* @brief   NotificationsScheduleControllerTest
+* @author  dev <dev@maarch.org>
+* @ingroup core
+*/
+
+use PHPUnit\Framework\TestCase;
+
+class NotificationScheduleControllerTest extends TestCase
+{
+    private static $id = null;
+
+    public function testCreateScript()
+    {
+        $NotificationScheduleController = new \Notification\controllers\NotificationScheduleController();
+
+        // CREATE FAIL
+        $environment = \Slim\Http\Environment::mock(['REQUEST_METHOD' => 'POST']);
+        $request     = \Slim\Http\Request::createFromEnvironment($environment);
+
+        $aArgs = [
+            "notification_sid" => "gaz",
+            "notification_id"  => "",
+        ];
+        $fullRequest = \httpRequestCustom::addContentInBody($aArgs, $request);
+
+        $response     = $NotificationScheduleController->createScriptNotification($fullRequest, new \Slim\Http\Response());
+        $responseBody = json_decode((string)$response->getBody());
+
+        $this->assertSame("notification_sid is not a numeric", $responseBody->errors[0]);
+        $this->assertSame("one of arguments is empty", $responseBody->errors[1]);
+
+
+        // CREATE
+        $environment = \Slim\Http\Environment::mock(['REQUEST_METHOD' => 'POST']);
+        $request     = \Slim\Http\Request::createFromEnvironment($environment);
+
+        $aArgs = [
+            "notification_sid" => 1,
+            "notification_id"  => "USERS",
+        ];
+        $fullRequest = \httpRequestCustom::addContentInBody($aArgs, $request);
+
+        $response     = $NotificationScheduleController->createScriptNotification($fullRequest, new \Slim\Http\Response());
+        $responseBody = json_decode((string)$response->getBody());
+
+        $this->assertSame(true, $responseBody);
+    }
+
+    public function testSaveCrontab()
+    {
+        $NotificationScheduleController = new \Notification\controllers\NotificationScheduleController();
+
+        $environment = \Slim\Http\Environment::mock(['REQUEST_METHOD' => 'GET']);
+        $request     = \Slim\Http\Request::createFromEnvironment($environment);
+
+        $response     = $NotificationScheduleController->get($request, new \Slim\Http\Response());
+        $responseBody = json_decode((string)$response->getBody());
+
+        // CREATE FAIL
+        $aArgs = [];
+        $aArgs = $responseBody->crontab;
+
+        $corePath = dirname(__FILE__, 3) . '/';
+        $newCrontab = [
+            "m"     => 12,
+            "h"     => 23,
+            "dom"   => "",
+            "mon"   => "*",
+            "dow"   => "*",
+            "cmd"   => $corePath . "modules/notifications/batch/scripts/notification_testtu.sh",
+            "state" => "new",
+        ];
+
+        array_push($aArgs, $newCrontab);
+        $fullRequest      = \httpRequestCustom::addContentInBody($aArgs, $request);
+        $response         = $NotificationScheduleController->saveCrontab($fullRequest, new \Slim\Http\Response());
+        $responseBodyFail = json_decode((string)$response->getBody());
+
+        $this->assertSame("dom is empty", $responseBodyFail->errors[0]);
+
+        // CREATE
+        $environment = \Slim\Http\Environment::mock(['REQUEST_METHOD' => 'POST']);
+        $request     = \Slim\Http\Request::createFromEnvironment($environment);
+
+        $aArgs = [];
+        $aArgs = $responseBody->crontab;
+
+        $corePath = dirname(__FILE__, 3) . '/';
+        $newCrontab = [
+            "m"     => 12,
+            "h"     => 23,
+            "dom"   => "*",
+            "mon"   => "*",
+            "dow"   => "*",
+            "cmd"   => $corePath . "modules/notifications/batch/scripts/notification_testtu.sh",
+            "state" => "new",
+        ];
+
+        array_push($aArgs, $newCrontab);
+        $fullRequest        = \httpRequestCustom::addContentInBody($aArgs, $request);
+        $response           = $NotificationScheduleController->saveCrontab($fullRequest, new \Slim\Http\Response());
+        $responseBodyCreate = json_decode((string)$response->getBody());
+
+        $this->assertSame(true, $responseBodyCreate);
+    }
+
+    public function testReadAll()
+    {
+        $environment = \Slim\Http\Environment::mock(['REQUEST_METHOD' => 'GET']);
+        $request     = \Slim\Http\Request::createFromEnvironment($environment);
+
+        $NotificationScheduleController = new \Notification\controllers\NotificationScheduleController();
+        $response                       = $NotificationScheduleController->get($request, new \Slim\Http\Response());
+        $responseBody                   = json_decode((string)$response->getBody());
+
+        $this->assertInternalType('array', $responseBody->crontab);
+        $this->assertInternalType('object', $responseBody->authorizedNotification);
+        $this->assertNotNull($responseBody->authorizedNotification);
+        $this->assertNotNull($responseBody->crontab);
+    }
+
+    public function testUpdateCrontab()
+    {
+        $NotificationScheduleController = new \Notification\controllers\NotificationScheduleController();
+        $environment = \Slim\Http\Environment::mock(['REQUEST_METHOD' => 'GET']);
+        $request     = \Slim\Http\Request::createFromEnvironment($environment);
+
+        $response     = $NotificationScheduleController->get($request, new \Slim\Http\Response());
+        $responseBody = json_decode((string)$response->getBody());
+
+        //  UPDATE
+        $environment = \Slim\Http\Environment::mock(['REQUEST_METHOD' => 'POST']);
+        $request     = \Slim\Http\Request::createFromEnvironment($environment);
+
+        $aArgs = [];
+        $aArgs = $responseBody->crontab;
+
+        $corePath = dirname(__FILE__, 3) . '/';
+
+        $aArgs[count($aArgs)-1] = [
+            "m"     => 35,
+            "h"     => 22,
+            "dom"   => "*",
+            "mon"   => "*",
+            "dow"   => "*",
+            "cmd"   => $corePath . "modules/notifications/batch/scripts/notification_testtu.sh",
+            "state" => "normal",
+        ];
+
+        $fullRequest = \httpRequestCustom::addContentInBody($aArgs, $request);
+
+        $response     = $NotificationScheduleController->saveCrontab($fullRequest, new \Slim\Http\Response());
+        $responseBody = json_decode((string)$response->getBody());
+
+        $this->assertSame(true, $responseBody);
+
+        $environment = \Slim\Http\Environment::mock(['REQUEST_METHOD' => 'GET']);
+        $request     = \Slim\Http\Request::createFromEnvironment($environment);
+
+        $response     = $NotificationScheduleController->get($request, new \Slim\Http\Response());
+        $responseBody = json_decode((string)$response->getBody());
+
+        $this->assertSame('35', $responseBody->crontab[count($responseBody->crontab)-1]->m);
+        $this->assertSame('22', $responseBody->crontab[count($responseBody->crontab)-1]->h);
+    }
+
+    public function testDelete()
+    {
+        // DELETE FAIL
+        $NotificationScheduleController = new \Notification\controllers\NotificationScheduleController();
+
+        $environment = \Slim\Http\Environment::mock(['REQUEST_METHOD' => 'GET']);
+        $request     = \Slim\Http\Request::createFromEnvironment($environment);
+
+        $response     = $NotificationScheduleController->get($request, new \Slim\Http\Response());
+        $responseBody = json_decode((string)$response->getBody());
+
+        $aArgs = [];
+        $aArgs = $responseBody->crontab;
+
+        foreach ($aArgs as $id => $value) {
+            if ($value->cmd == dirname(__FILE__, 3) . '/' . "modules/notifications/batch/scripts/notification_testtu.sh") {
+                $aArgs[$id]->state = "hidden";
+            }
+        }
+
+        $environment = \Slim\Http\Environment::mock(['REQUEST_METHOD' => 'POST']);
+        $request     = \Slim\Http\Request::createFromEnvironment($environment);
+        $fullRequest = \httpRequestCustom::addContentInBody($aArgs, $request);
+
+        $response         = $NotificationScheduleController->saveCrontab($fullRequest, new \Slim\Http\Response());
+        $responseBodyFail = json_decode((string)$response->getBody());
+
+        $this->assertSame("Problem with crontab", $responseBodyFail->errors);
+
+        // DELETE
+        $aArgs = [];
+        $aArgs = $responseBody->crontab;
+
+        foreach ($aArgs as $id => $value) {
+            if ($value->cmd == dirname(__FILE__, 3) . '/' . "modules/notifications/batch/scripts/notification_testtu.sh") {
+                $aArgs[$id]->state = "deleted";
+            }
+        }
+
+        $fullRequest = \httpRequestCustom::addContentInBody($aArgs, $request);
+
+        $response     = $NotificationScheduleController->saveCrontab($fullRequest, new \Slim\Http\Response());
+        $responseBody = json_decode((string)$response->getBody());
+
+        $this->assertSame(true, $responseBody);
+    }
+}
diff --git a/core/Test/StatusControllerTest.php b/core/Test/StatusControllerTest.php
index 740b482174af77a942a950684b26c835c06c91ae..f844fa516af24766473a3f05bd4bbe304a9c71ce 100755
--- a/core/Test/StatusControllerTest.php
+++ b/core/Test/StatusControllerTest.php
@@ -7,8 +7,6 @@
 *
 */
 
-namespace MaarchTest;
-
 use PHPUnit\Framework\TestCase;
 
 class StatusControllerTest extends TestCase
diff --git a/phpunit.xml b/phpunit.xml
index aa29560a533eb339fdaba4487a264a36b1d1ed46..2354743bb2abbcfa3016b2ec600f1acde706e44b 100755
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -6,6 +6,7 @@
             <file>core/Test/ActionControllerTest.php</file>
             <file>core/Test/BasketControllerTest.php</file>
             <file>core/Test/NotificationControllerTest.php</file>
+            <file>core/Test/NotificationScheduleControllerTest.php</file>
             <file>core/Test/ParameterControllerTest.php</file>
             <file>core/Test/PriorityControllerTest.php</file>
             <file>core/Test/ReportControllerTest.php</file>
diff --git a/src/app/notification/controllers/NotificationScheduleController.php b/src/app/notification/controllers/NotificationScheduleController.php
new file mode 100644
index 0000000000000000000000000000000000000000..e9dc8ef66467532a7c90ec68cbff8a9318fcb361
--- /dev/null
+++ b/src/app/notification/controllers/NotificationScheduleController.php
@@ -0,0 +1,283 @@
+<?php
+
+/**
+* Copyright Maarch since 2008 under licence GPLv3.
+* See LICENCE.txt file at the root folder for more details.
+* This file is part of Maarch software.
+*
+*/
+
+/**
+* @brief Notifications Schedule Controller
+* @author dev@maarch.org
+* @ingroup notifications
+*/
+
+namespace Notification\controllers;
+
+use History\controllers\HistoryController;
+use Respect\Validation\Validator;
+use Notification\models\NotificationModel;
+use Core\Models\ServiceModel;
+use Slim\Http\Request;
+use Slim\Http\Response;
+use SrcCore\models\CoreConfigModel;
+
+class NotificationScheduleController
+{
+    public function get(Request $request, Response $response)
+    {
+        if (!ServiceModel::hasService(['id' => 'admin_notif', 'userId' => $GLOBALS['userId'], 'location' => 'notifications', 'type' => 'admin'])) {
+            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
+        }
+
+        return $response->withJson([
+            'crontab'                => self::getCrontab(),
+            'authorizedNotification' => self::getAuthorizedNotifications(),
+        ]);
+    }
+    
+    public function saveCrontab(Request $request, Response $response)
+    {
+        if (!ServiceModel::hasService(['id' => 'admin_notif', 'userId' => $GLOBALS['userId'], 'location' => 'notifications', 'type' => 'admin'])) {
+            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
+        }
+
+        $data = $request->getParams();
+        if (!$this->checkCrontab($data)) {
+            return $response->withStatus(500)->withJson(['errors' => 'Problem with crontab']);
+        }
+
+        foreach ($data as $id => $cronValue) {
+            foreach ($cronValue as $key => $value) {
+                if (!Validator::notEmpty()->validate($value)) {
+                    $errors[] = $key." is empty";
+                }
+                if ($key != "cmd" && $key != "state" && !Validator::intVal()->validate($value) && $value != "*") {
+                    $errors[] = "wrong format for ".$key;
+                }
+            }
+        }
+        if (!empty($errors)) {
+            return $response->withStatus(500)->withJson(['errors' => $errors]);
+        }
+
+        $aCrontab = self::getCrontab(false);
+
+        $file = [];
+        foreach ($data as $id => $cronValue) {
+            if ($cronValue['state'] == 'hidden') {
+                $file[$id] = "{$aCrontab[$id]['m']}\t{$aCrontab[$id]['h']}\t{$aCrontab[$id]['dom']}\t{$aCrontab[$id]['mon']}\t{$aCrontab[$id]['dow']}\t{$aCrontab[$id]['cmd']}";
+            } elseif ($cronValue['state'] != 'deleted') {
+                $file[$id] = "{$cronValue['m']}\t{$cronValue['h']}\t{$cronValue['dom']}\t{$cronValue['mon']}\t{$cronValue['dow']}\t{$cronValue['cmd']}";
+            }
+        }
+
+        $output = '';
+
+        if (isset($file)) {
+            foreach ($file as $l) {
+                $output .= "$l\n";
+            }
+        }
+
+        $output = preg_replace("!\n+$!", "\n", $output);
+        file_put_contents('/tmp/crontab.plain', print_r($file, true));
+        file_put_contents('/tmp/crontab.txt', $output);
+
+        exec('crontab /tmp/crontab.txt');
+
+        return $response->withJson(true);
+    }
+
+    protected static function getCrontab($getHiddenValue = true)
+    {
+        $crontab  = shell_exec('crontab -l');
+        $lines    = explode("\n", $crontab);
+        $data     = array();
+        $customId = CoreConfigModel::getCustomId();
+        $corePath = dirname(__FILE__, 5) . '/';
+
+        foreach ($lines as $cronLine) {
+            $cronLine = trim($cronLine);
+            if (strpos($cronLine, '#') !== false) {
+                $cronLine = substr($cronLine, 0, strpos($cronLine, '#'));
+            }
+            if (empty($cronLine)) {
+                continue;
+            }
+            $cronLine = preg_replace('![ \t]+!', ' ', $cronLine);
+            if ($cronLine[0] == '@') {
+                list($time, $cmd) = explode(' ', $cronLine, 2);
+            } else {
+                list($m, $h, $dom, $mon, $dow, $cmd) = explode(' ', $cronLine, 6);
+            }
+
+            if ($customId <> '') {
+                $pathToFolow = $corePath . 'custom/'.$customId . '/';
+            } else {
+                $pathToFolow = $corePath;
+            }
+
+            $state = "normal";
+            if (strpos($cmd, $pathToFolow.'modules/notifications/batch/scripts/') !== 0 && $getHiddenValue) {
+                $cmd   = "hidden";
+                $state = 'hidden';
+            }
+
+            $data[] = array(
+                'm'     => $m,
+                'h'     => $h,
+                'dom'   => $dom,
+                'mon'   => $mon,
+                'dow'   => $dow,
+                'cmd'   => $cmd,
+                'state' => $state
+            );
+        }
+        return $data;
+    }
+
+    protected static function getAuthorizedNotifications()
+    {
+        $aNotification      = NotificationModel::getEnableNotifications(['select' => ['notification_sid', 'description']]);
+        $notificationsArray = array();
+        $customId           = CoreConfigModel::getCustomId();
+        $corePath           = dirname(__FILE__, 5) . '/';
+
+        foreach ($aNotification as $result) {
+            $filename = "notification";
+            if (isset($customId) && $customId<>"") {
+                $filename.="_".str_replace(" ", "", $customId);
+            }
+            $filename.="_".$result['notification_sid'].".sh";
+
+            if ($customId <> '') {
+                $pathToFolow = $corePath . 'custom/'.$customId . '/';
+            } else {
+                $pathToFolow = $corePath;
+            }
+
+            $path = $pathToFolow.'modules/notifications/batch/scripts/'.$filename;
+
+            if (file_exists($path)) {
+                $notificationsArray[$path] = $result['description'];
+            }
+        }
+        
+        return $notificationsArray;
+    }
+
+    protected static function checkCrontab($crontabToSave)
+    {
+        $customId          = CoreConfigModel::getCustomId();
+        $crontabBeforeSave = self::getCrontab();
+        $corePath          = dirname(__FILE__, 5) . '/';
+        foreach ($crontabToSave as $id => $cronValue) {
+            if ($cronValue['state'] != "hidden" && $crontabBeforeSave[$id]['state'] == "hidden") {
+                $returnValue = false;
+                break;
+            } elseif ($cronValue['state'] == "hidden" && $crontabBeforeSave[$id]['state'] != "hidden") {
+                $returnValue = false;
+                break;
+            } elseif ($cronValue['state'] == "new" || $cronValue['state'] == "normal") {
+                if ($customId <> '') {
+                    $pathToFolow = $corePath . 'custom/'.$customId . '/';
+                } else {
+                    $pathToFolow = $corePath;
+                }
+                $returnValue = true;
+                if (strpos($crontabToSave[$id]['cmd'], $pathToFolow.'modules/notifications/batch/scripts/') !== 0) {
+                    $returnValue = false;
+                    break;
+                }
+            } else {
+                $returnValue = true;
+            }
+        }
+
+        return $returnValue;
+    }
+
+    public function createScriptNotification(Request $request, Response $response)
+    {
+        if (!ServiceModel::hasService(['id' => 'admin_notif', 'userId' => $GLOBALS['userId'], 'location' => 'notifications', 'type' => 'admin'])) {
+            return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
+        }
+
+        $errors = [];
+        $data = $request->getParams();
+        if (!Validator::intVal()->validate($data['notification_sid'])) {
+            $errors[] = 'notification_sid is not a numeric';
+        }
+        if (!Validator::notEmpty()->validate($data['notification_sid']) ||
+            !Validator::notEmpty()->validate($data['notification_id'])) {
+            $errors[] = 'one of arguments is empty';
+        }
+
+        if (!empty($errors)) {
+            return $response
+                ->withStatus(500)
+                ->withJson(['errors' => $errors]);
+        }
+
+        $notification_sid = $data['notification_sid'];
+        $notification_id  = $data['notification_id'];
+
+        //Creer le script sh pour les notifications
+        $filename = "notification";
+        $customId = CoreConfigModel::getCustomId();
+        if (isset($customId) && $customId<>"") {
+            $filename.="_".str_replace(" ", "", $customId);
+        }
+        $filename.="_".$notification_sid.".sh";
+
+        $corePath = dirname(__FILE__, 5) . '/';
+
+        if (file_exists($corePath. 'custom/'.$customId .'/modules/notifications/batch/config/config.xml')) {
+            $ConfigNotif = $corePath. 'custom/'. $customId .'/modules/notifications/batch/config/config.xml';
+        } elseif (file_exists($corePath. 'custom/'. $customId .'/modules/notifications/batch/config/config_'.$customId.'.xml')) {
+            $ConfigNotif = $corePath. 'custom/'. $customId .'/modules/notifications/batch/config/config_'.$customId.'.xml';
+        } elseif (file_exists($corePath. 'modules/notifications/batch/config/config_'.$customId.'.xml')) {
+            $ConfigNotif = $corePath. 'modules/notifications/batch/config/config_'.$customId.'.xml';
+        } else {
+            $ConfigNotif = $corePath. 'modules/notifications/batch/config/config.xml';
+        }
+        
+        if ($customId <> '') {
+            $pathToFolow = $corePath . 'custom/'.$customId . '/';
+            if (!file_exists($pathToFolow.'modules/notifications/batch/scripts/')) {
+                mkdir($pathToFolow.'modules/notifications/batch/scripts/', 0777, true);
+            }
+            $file_open = fopen($pathToFolow.'modules/notifications/batch/scripts/'.$filename, 'w+');
+        } else {
+            $pathToFolow = $corePath;
+            $file_open = fopen($pathToFolow.'modules/notifications/batch/scripts/'.$filename, 'w+');
+        }
+
+        fwrite($file_open, '#!/bin/sh');
+        fwrite($file_open, "\n");
+        fwrite($file_open, 'path=\''.$corePath.'modules/notifications/batch/\'');
+        fwrite($file_open, "\n");
+        fwrite($file_open, 'cd $path');
+        fwrite($file_open, "\n");
+        if ($notification_id == 'BASKETS') {
+            fwrite($file_open, 'php \'basket_event_stack.php\' -c '.$ConfigNotif.' -n '.$notification_id);
+        } elseif ($notification_id == 'RELANCE1' || $notification_id == 'RELANCE2' || $notification_id == 'RET1' || $notification_id == 'RET2') {
+            fwrite($file_open, 'php \'stack_letterbox_alerts.php\' -c '.$ConfigNotif);
+            fwrite($file_open, "\n");
+            fwrite($file_open, 'php \'process_event_stack.php\' -c '.$ConfigNotif.' -n '.$notification_id);
+        } else {
+            fwrite($file_open, 'php \'process_event_stack.php\' -c '.$ConfigNotif.' -n '.$notification_id);
+        }
+        fwrite($file_open, "\n");
+        fwrite($file_open, 'cd $path');
+        fwrite($file_open, "\n");
+        fwrite($file_open, 'php \'process_email_stack.php\' -c '.$ConfigNotif);
+        fwrite($file_open, "\n");
+        fclose($file_open);
+        shell_exec("chmod +x " . escapeshellarg($pathToFolow . "modules/notifications/batch/scripts/" . $filename));
+
+        return $response->withJson(true);
+    }
+}