diff --git a/readme.md b/readme.md
index 842b82732fb6795b10be63d4985bdf9df5bb184c..ccf5b24c9408192a70bef30efb01cc3f952f3108 100644
--- a/readme.md
+++ b/readme.md
@@ -175,6 +175,16 @@ server {
   - extract static files and make them available via nginx: `./manage.py collectstatic`
   - you start application by starting gunicorn and nginx: `service gunicorn start`, `service nginx start`
 
+## Cron jobs (weekly emails)
+
+If weekly emails are required then cron must be edited to fire periodically django function that send emails.
+
+```
+> crontab -e
+SHELL=/bin/bash
+*/30 * * * * source /var/www/scheduling-system/env/bin/activate && python /var/www/scheduling-system/smash/manage.py runcrons >> /var/log/django-cronjob.log 2>&1
+```
+
 ## Operations
 
 ### Public holidays
@@ -191,4 +201,4 @@ example:
 
 ```
 ./manage.py holidays 2017 2018 2019
-```
\ No newline at end of file
+```
diff --git a/requirements.txt b/requirements.txt
index 7c8c8a256eade03dd1b45e814f2edf0593fd446b..0dcc0503db9fadbba8cf9f7f203abd80d3926b62 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,4 +5,5 @@ psycopg2==2.6.2
 pytz==2016.10
 lxml==3.7.3
 python-docx==0.8.6
-django-cleanup==0.4.2
\ No newline at end of file
+django-cleanup==0.4.2
+django_cron==0.5.0
diff --git a/smash/smash/settings.py b/smash/smash/settings.py
index 48fbaf41986bb03af7835e85f076684e91bdbc90..c19c846a7b8e0e037efd8015b67e5c750ac8129c 100644
--- a/smash/smash/settings.py
+++ b/smash/smash/settings.py
@@ -22,13 +22,6 @@ ALLOWED_HOSTS = ['prc.parkinson.lu', 'localhost']
 
 DEBUG = True
 
-EMAIL_HOST = 'smtp.uni.lu'
-EMAIL_HOST_USER = ''
-EMAIL_HOST_PASSWORD = ''
-EMAIL_PORT = 25
-EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
-DEFAULT_FROM_EMAIL = 'prc-scheduling-admin@uni.lu'
-
 # Application definition
 
 INSTALLED_APPS = [
@@ -39,6 +32,7 @@ INSTALLED_APPS = [
     'django.contrib.messages',
     'django.contrib.staticfiles',
     'django_cleanup',
+    'django_cron',
     'debug_toolbar',
     'web'
 ]
@@ -72,6 +66,10 @@ TEMPLATES = [
     },
 ]
 
+CRON_CLASSES = [
+    'web.views.kit.KitRequestEmailSendJob'
+]
+
 # Password validation
 # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
 
diff --git a/smash/web/api_views/configuration.py b/smash/web/api_views/configuration.py
index 305cbef01bc87a15ddec0ca735c18b63866a3b9f..c332d385438cb274b8d8f0509f975d218ddd9739 100644
--- a/smash/web/api_views/configuration.py
+++ b/smash/web/api_views/configuration.py
@@ -52,7 +52,13 @@ def update_configuration_item(request):
         })
     item = items[0]
     item.value = value
-    item.save()
-    return JsonResponse({
-        "status": "ok",
-    })
+    if ConfigurationItem.is_valid(item):
+        item.save()
+        return JsonResponse({
+            "status": "ok",
+        })
+    else:
+        return JsonResponse({
+            "status": "error",
+            "message": ConfigurationItem.validation_error(item)
+        })
diff --git a/smash/web/models/configuration_item.py b/smash/web/models/configuration_item.py
index 2d982b06aa3f37b9cbd78310b39d3ee61bc3de9b..0129315ae6f6b1286471ba903db2b447035bcdd2 100644
--- a/smash/web/models/configuration_item.py
+++ b/smash/web/models/configuration_item.py
@@ -1,6 +1,11 @@
 # coding=utf-8
+import re
 from django.db import models
 
+from web.models.constants import CANCELLED_APPOINTMENT_COLOR_CONFIGURATION_TYPE, \
+    NO_SHOW_APPOINTMENT_COLOR_CONFIGURATION_TYPE, KIT_EMAIL_HOUR_CONFIGURATION_TYPE, \
+    KIT_EMAIL_DAY_OF_WEEK_CONFIGURATION_TYPE
+
 
 class ConfigurationItem(models.Model):
     type = models.CharField(max_length=50,
@@ -21,3 +26,24 @@ class ConfigurationItem(models.Model):
 
     def __unicode__(self):
         return "%s %s" % (self.name, self.value)
+
+    @staticmethod
+    def is_valid(item):
+        message = ConfigurationItem.validation_error(item)
+        return message == ""
+
+    @staticmethod
+    def validation_error(item):
+        pattern = None
+        if item.type == CANCELLED_APPOINTMENT_COLOR_CONFIGURATION_TYPE \
+                or item.type == NO_SHOW_APPOINTMENT_COLOR_CONFIGURATION_TYPE:
+            pattern = "^#[0-9a-fA-F]+$"
+        if item.type == KIT_EMAIL_HOUR_CONFIGURATION_TYPE:
+            pattern = "^[0-9]{2}:[0-9]{2}$"
+        if item.type == KIT_EMAIL_DAY_OF_WEEK_CONFIGURATION_TYPE:
+            pattern = "^(MONDAY|TUESDAY|WEDNESDAY|THURSDAY|FRIDAY|SATURDAY|SUNDAY)$"
+        if pattern is not None:
+            if not re.compile(pattern).match(item.value):
+                return "Invalid value of param: " + item.name + ". It should match regex pattern: " + pattern
+
+        return ""
diff --git a/smash/web/models/constants.py b/smash/web/models/constants.py
index fb01625467218e48e9111f775824a84e93372843..6d5fbeef623b083661c57842689254ac95f12fde 100644
--- a/smash/web/models/constants.py
+++ b/smash/web/models/constants.py
@@ -53,3 +53,11 @@ MAIL_TEMPLATE_CONTEXT_CHOICES = (
     (MAIL_TEMPLATE_CONTEXT_VISIT, 'Visit'),
 )
 LOCALE_CHOICES = [(value, value) for value in sorted(locale.windows_locale.values())]
+
+MONDAY_AS_DAY_OF_WEEK = 1
+TUESDAY_AS_DAY_OF_WEEK = 2
+WEDNESDAY_AS_DAY_OF_WEEK = 3
+THURSDAY_AS_DAY_OF_WEEK = 4
+FRIDAY_AS_DAY_OF_WEEK = 5
+SATURDAY_AS_DAY_OF_WEEK = 6
+SUNDAY_AS_DAY_OF_WEEK = 7
diff --git a/smash/web/templates/equipment_and_rooms/kit_requests.html b/smash/web/templates/equipment_and_rooms/kit_requests.html
index 25ef9d78762b0488ff0ade18605068303ee22c6b..764d3b4bcc8a1e4b7d2decf037b3beaaa8988499 100644
--- a/smash/web/templates/equipment_and_rooms/kit_requests.html
+++ b/smash/web/templates/equipment_and_rooms/kit_requests.html
@@ -88,10 +88,10 @@
                     <div class="col-sm-12">
                         {% if end_date == None %}
                             <a href="{% url 'web.views.kit_requests_send_mail' start_date|date:"Y-m-d" %}"
-                               class="btn btn-block btn-default">Show email content</a>
+                               class="btn btn-block btn-default">Send email</a>
                         {% else %}
                             <a href="{% url 'web.views.kit_requests_send_mail' start_date|date:"Y-m-d" end_date|date:"Y-m-d" %}"
-                               class="btn btn-block btn-default">Show email content</a>
+                               class="btn btn-block btn-default">Send email</a>
                         {% endif %}
                     </div>
                 </div><!-- /.box-footer -->
diff --git a/smash/web/tests/test_api_configuration_item.py b/smash/web/tests/test_api_configuration_item.py
index a550ff27cb88b64d95630dc0c032ffaee2695d13..78ef0d1674706159f8faca41fd658ba441a65ef6 100644
--- a/smash/web/tests/test_api_configuration_item.py
+++ b/smash/web/tests/test_api_configuration_item.py
@@ -6,6 +6,9 @@ from django.urls import reverse
 from web.models import ConfigurationItem
 from web.tests.functions import create_configuration_item
 from . import LoggedInTestCase
+from web.models.constants import CANCELLED_APPOINTMENT_COLOR_CONFIGURATION_TYPE, \
+    NO_SHOW_APPOINTMENT_COLOR_CONFIGURATION_TYPE, KIT_EMAIL_HOUR_CONFIGURATION_TYPE, \
+    KIT_EMAIL_DAY_OF_WEEK_CONFIGURATION_TYPE
 
 
 class TestConfigurationItemApi(LoggedInTestCase):
@@ -31,3 +34,21 @@ class TestConfigurationItemApi(LoggedInTestCase):
         self.assertEqual(response.status_code, 200)
         updated_item = ConfigurationItem.objects.get(id=item.id)
         self.assertEqual(new_val, updated_item.value)
+
+    def test_configuration_modify_CANCELLED_APPOINTMENT_COLOR_CONFIGURATION_TYPE_invalid_value(self):
+        item = ConfigurationItem.objects.get(type=CANCELLED_APPOINTMENT_COLOR_CONFIGURATION_TYPE)
+        invalid_val = 'invalid color'
+
+        response = self.client.get(reverse('web.api.update_configuration_item'), {'id': item.id, 'value': invalid_val})
+        self.assertEqual(response.status_code, 200)
+        updated_item = ConfigurationItem.objects.get(type=CANCELLED_APPOINTMENT_COLOR_CONFIGURATION_TYPE)
+        self.assertNotEqual(invalid_val, updated_item.value)
+
+    def test_configuration_modify_CANCELLED_APPOINTMENT_COLOR_CONFIGURATION_TYPE_valid_value(self):
+        item = ConfigurationItem.objects.get(type=CANCELLED_APPOINTMENT_COLOR_CONFIGURATION_TYPE)
+        invalid_val = '#FFFFFF'
+
+        response = self.client.get(reverse('web.api.update_configuration_item'), {'id': item.id, 'value': invalid_val})
+        self.assertEqual(response.status_code, 200)
+        updated_item = ConfigurationItem.objects.get(type=CANCELLED_APPOINTMENT_COLOR_CONFIGURATION_TYPE)
+        self.assertEqual(invalid_val, updated_item.value)
diff --git a/smash/web/tests/test_process_file.py b/smash/web/tests/test_process_file.py
index 7d00165404acd575efa951f4b48f333ad7c2817a..f46bcac4266a002dcd993cf59d73cb0fb78c82c8 100644
--- a/smash/web/tests/test_process_file.py
+++ b/smash/web/tests/test_process_file.py
@@ -26,7 +26,7 @@ class TestDocxProcessor(TestCase):
             "##ADDRESS2##": "61-234, Poznan",
             "##COUNTRY##": "POLAND",
             "##CONTENT##": "1",
-            "##DATE##": datetime.datetime.now().date().strftime("%A %-d %B %Y"),
+            "##DATE##": datetime.datetime.now().date().strftime("%A %d %B %Y"),
         }
         process_file(template_path, output_path, changes)
         self.assertTrue(os.path.isfile(output_path))
diff --git a/smash/web/tests/test_view_kit_request.py b/smash/web/tests/test_view_kit_request.py
index d2bbebcdd27ab0e22d1decf2fd4f32910ff61141..4a061d4a9c4d5ec17ae6eff7bba66656766fe59a 100644
--- a/smash/web/tests/test_view_kit_request.py
+++ b/smash/web/tests/test_view_kit_request.py
@@ -1,9 +1,11 @@
 import datetime
 
+from django.core import mail
 from django.urls import reverse
 
-from functions import create_appointment_type, create_appointment
+from functions import create_appointment_type, create_appointment, create_visit
 from web.models import Item, Appointment, AppointmentTypeLink
+from web.views.kit import get_kit_requests
 from web.views.notifications import get_today_midnight_date
 from . import LoggedInTestCase
 
@@ -25,7 +27,6 @@ class ViewFunctionsTests(LoggedInTestCase):
         appointment.save()
         AppointmentTypeLink.objects.create(appointment=appointment, appointment_type=appointment_type)
 
-
         response = self.client.get(reverse('web.views.kit_requests'))
         self.assertEqual(response.status_code, 200)
 
@@ -61,8 +62,57 @@ class ViewFunctionsTests(LoggedInTestCase):
         appointment.save()
         AppointmentTypeLink.objects.create(appointment=appointment, appointment_type=appointment_type)
 
-
         response = self.client.get(reverse('web.views.kit_requests'))
         self.assertEqual(response.status_code, 200)
 
         self.assertTrue(item_name in response.content)
+
+    def test_kit_requests_order(self):
+        item_name = "Test item to be ordered"
+        item = Item.objects.create(disposable=True, name=item_name)
+        appointment_type = create_appointment_type()
+        appointment_type.required_equipment.add(item)
+        appointment_type.save()
+
+        visit = create_visit();
+
+        appointment1 = create_appointment(visit)
+        appointment1.datetime_when = get_today_midnight_date() + datetime.timedelta(days=3)
+        appointment1.save()
+        AppointmentTypeLink.objects.create(appointment=appointment1, appointment_type=appointment_type)
+
+        appointment2 = create_appointment(visit)
+        appointment2.datetime_when = get_today_midnight_date() + datetime.timedelta(days=4)
+        appointment2.save()
+        AppointmentTypeLink.objects.create(appointment=appointment2, appointment_type=appointment_type)
+
+        appointment3 = create_appointment(visit)
+        appointment3.datetime_when = get_today_midnight_date() + datetime.timedelta(days=2)
+        appointment3.save()
+        AppointmentTypeLink.objects.create(appointment=appointment3, appointment_type=appointment_type)
+
+        result = get_kit_requests(self.user)
+        self.assertEqual(appointment3, result['appointments'][0])
+        self.assertEqual(appointment1, result['appointments'][1])
+        self.assertEqual(appointment2, result['appointments'][2])
+
+
+def test_kit_requests_send_email(self):
+    item_name = "Test item to be ordered"
+    item = Item.objects.create(disposable=True, name=item_name)
+    appointment_type = create_appointment_type()
+    appointment_type.required_equipment.add(item)
+    appointment_type.save()
+
+    appointment = create_appointment()
+    appointment.datetime_when = get_today_midnight_date() + datetime.timedelta(days=2)
+    appointment.save()
+    AppointmentTypeLink.objects.create(appointment=appointment, appointment_type=appointment_type)
+
+    response = self.client.get(reverse('web.views.kit_requests_send_mail',
+                                       kwargs={'start_date': str(get_today_midnight_date().strftime("%Y-%m-%d"))}))
+    self.assertEqual(response.status_code, 200)
+
+    self.assertTrue(item_name in response.content)
+
+    self.assertEqual(1, len(mail.outbox))
diff --git a/smash/web/views/doctor.py b/smash/web/views/doctor.py
index 09031a36050d59d970c8c57942a188726f88021e..43a7465552e55183bf4104bc53c667b3f4e5876b 100644
--- a/smash/web/views/doctor.py
+++ b/smash/web/views/doctor.py
@@ -4,15 +4,8 @@ from django.shortcuts import redirect, get_object_or_404
 from . import wrap_response
 from ..forms import WorkerAddForm, WorkerEditForm, WorkerDetailForm
 from ..models import Worker, Availability
-
-MONDAY_AS_DAY_OF_WEEK = 1
-TUESDAY_AS_DAY_OF_WEEK = 2
-WEDNESDAY_AS_DAY_OF_WEEK = 3
-THURSDAY_AS_DAY_OF_WEEK = 4
-FRIDAY_AS_DAY_OF_WEEK = 5
-SATURDAY_AS_DAY_OF_WEEK = 6
-SUNDAY_AS_DAY_OF_WEEK = 7
-
+from ..models.constants import MONDAY_AS_DAY_OF_WEEK, TUESDAY_AS_DAY_OF_WEEK, WEDNESDAY_AS_DAY_OF_WEEK, \
+    THURSDAY_AS_DAY_OF_WEEK, FRIDAY_AS_DAY_OF_WEEK, SATURDAY_AS_DAY_OF_WEEK, SUNDAY_AS_DAY_OF_WEEK
 
 def doctors(request):
     doctors_list = Worker.objects.order_by('-last_name')
diff --git a/smash/web/views/kit.py b/smash/web/views/kit.py
index f4a7b9e2e2065a6b1f1d6ff36f4ea5e7301f2547..98eb536f942b5737a3765ecd461c1ae09e7723cc 100644
--- a/smash/web/views/kit.py
+++ b/smash/web/views/kit.py
@@ -1,12 +1,27 @@
 # coding=utf-8
+
 import datetime
+import locale
+import platform
+import sys
+import time
+import traceback
 
+import pytz
+from django.contrib import messages
 from django.utils.dateparse import parse_datetime
+from django_cron import CronJobBase, Schedule
+from django_cron.models import CronJobLog
 
 from notifications import get_filter_locations, get_today_midnight_date
+from web.models import ConfigurationItem, Language, Worker
+from web.models.constants import KIT_EMAIL_HOUR_CONFIGURATION_TYPE, \
+    KIT_EMAIL_DAY_OF_WEEK_CONFIGURATION_TYPE
+from web.models.constants import KIT_RECIPIENT_EMAIL_CONFIGURATION_TYPE
 from . import wrap_response
 from ..forms import KitRequestForm
 from ..models import AppointmentType, Appointment
+from ..smash_email import EmailSender
 
 
 def get_kit_requests(user, start_date=None, end_date=None):
@@ -16,8 +31,12 @@ def get_kit_requests(user, start_date=None, end_date=None):
     else:
         if isinstance(start_date, str):
             start_date = parse_datetime(start_date)
+        if isinstance(start_date, unicode):
+            start_date = datetime.datetime.strptime(start_date, '%Y-%m-%d')
         if (end_date is not None) and (isinstance(end_date, str)):
             end_date = parse_datetime(end_date)
+        if (end_date is not None) and (isinstance(end_date, unicode)):
+            end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d')
 
     appointment_types = AppointmentType.objects.filter(required_equipment__disposable=True)
 
@@ -26,7 +45,8 @@ def get_kit_requests(user, start_date=None, end_date=None):
         datetime_when__gt=start_date,
         location__in=get_filter_locations(user),
         status=Appointment.APPOINTMENT_STATUS_SCHEDULED,
-    )
+    ).order_by('datetime_when')
+
     if end_date is not None:
         appointments = appointments.filter(datetime_when__lt=end_date)
 
@@ -58,6 +78,98 @@ def kit_requests(request):
     return wrap_response(request, 'equipment_and_rooms/kit_requests.html', get_kit_requests_data(request))
 
 
+def send_mail(data):
+    end_date_str = " end of time"
+    if data["end_date"] is not None:
+        end_date_str = data["end_date"].strftime('%Y-%m-%d')
+    title = "Kits required between " + data["start_date"].strftime('%Y-%m-%d') + " and " + end_date_str
+
+    cell_style = "padding: 8px; line-height: 1.42857143; vertical-align: top; " \
+                 "font-size: 14px; font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,Arial,sans-serif;"
+
+    email_body = "<h1>" + title + "</h1>"
+    email_body += '<table style="border: 1px solid #f4f4f4;border-spacing: 0;border-collapse: collapse;">' \
+                  '<thead><tr><th>Date</th><th>Kits</th><th>Location</th><th>Person responsible</th></tr></thead>'
+    email_body += "<tbody>"
+
+    even = True
+    for appointment in data["appointments"]:
+        row_style = ""
+        even = not even
+        if even:
+            row_style = ' background-color: #f9f9f9;'
+        email_body += "<tr style='" + row_style + "'>"
+        email_body += "<td style='" + cell_style + "'>" + appointment.datetime_when.strftime('%Y-%m-%d %H:%M') + "</td>"
+        email_body += "<td style='" + cell_style + "'>"
+        for type in appointment.appointment_types.all():
+            for item in type.required_equipment.all():
+                if item.disposable:
+                    email_body += item.name + ", "
+        email_body += "</td>"
+        email_body += "<td style='" + cell_style + "'>" + str(appointment.location) + "</td>"
+        email_body += "<td style='" + cell_style + "'>" + str(appointment.worker_assigned) + "</td>"
+        email_body += "</tr>"
+    email_body += "</tbody></table>"
+    recipients = ConfigurationItem.objects.get(type=KIT_RECIPIENT_EMAIL_CONFIGURATION_TYPE).value
+    cc_recipients = []
+    EmailSender().send_email(title, email_body, recipients, cc_recipients)
+
+
 def kit_requests_send_mail(request, start_date, end_date=None):
-    return wrap_response(request, 'equipment_and_rooms/kit_requests_send_mail.html',
-                         get_kit_requests_data(request, start_date, end_date))
+    data = get_kit_requests_data(request, start_date, end_date)
+    try:
+        send_mail(data)
+        messages.add_message(request, messages.SUCCESS, 'Mail sent')
+    except:
+        messages.add_message(request, messages.ERROR, 'There was problem with sending email')
+    return wrap_response(request, 'equipment_and_rooms/kit_requests.html', get_kit_requests_data(request))
+
+
+class KitRequestEmailSendJob(CronJobBase):
+    RUN_EVERY_MINS = 1
+    schedule = Schedule(run_every_mins=RUN_EVERY_MINS)
+    code = 'web.kit_request_weekly_email'  # a unique code
+
+    def do(self):
+        now = datetime.datetime.utcnow()
+        hour = int(ConfigurationItem.objects.get(
+            type=KIT_EMAIL_HOUR_CONFIGURATION_TYPE).value.split(":")[0])
+        minute = int(ConfigurationItem.objects.get(
+            type=KIT_EMAIL_HOUR_CONFIGURATION_TYPE).value.split(":")[1])
+        # check if we sent email this day already
+        date = now.replace(hour=hour, minute=minute)
+        # TODO it's a hack assuming that we are in CEST
+        date = pytz.utc.localize(date - datetime.timedelta(minutes=122))
+        jobs = CronJobLog.objects.filter(code=KitRequestEmailSendJob.code, message="mail sent", start_time__gte=date)
+
+        if jobs.count() == 0:
+            print date
+            print datetime.datetime.now()
+            if pytz.utc.localize(datetime.datetime.utcnow()) > date:
+                if self.match_day_of_week():
+                    data = get_kit_requests(Worker.objects.create());
+                    send_mail(data);
+                    return "mail sent"
+                else:
+                    return "day of week doesn't match"
+            else:
+                return "too early"
+        else:
+            return "mail already sent"
+
+    def match_day_of_week(self):
+        user_day_of_week = ConfigurationItem.objects.get(type=KIT_EMAIL_DAY_OF_WEEK_CONFIGURATION_TYPE).value
+        language = Language.objects.get(name="English");
+        locale_name = language.locale
+        if platform.system() == 'Windows':
+            locale_name = language.windows_locale_name
+        try:
+            locale.setlocale(locale.LC_TIME, locale_name)
+        except:
+            print locale_name
+            traceback.print_exc(file=sys.stdout)
+
+        user_day_of_week_int = int(time.strptime(user_day_of_week, '%A').tm_wday) + 1
+        current_day_of_week_int = int(datetime.datetime.now().strftime("%w"));
+
+        return user_day_of_week_int == current_day_of_week_int