diff --git a/smash/web/api_urls.py b/smash/web/api_urls.py
index 56f9dc4f46cbac9ca9c7ab622abd000aadbe9aa5..372aa370c6f24960f3122b1c18b24e8494fac9a0 100644
--- a/smash/web/api_urls.py
+++ b/smash/web/api_urls.py
@@ -16,7 +16,7 @@ Including another URLconf
 from django.conf.urls import url
 
 from web.api_views import worker, location, subject, appointment_type, appointment, configuration, daily_planning, \
-    redcap, flying_team, visit
+    redcap, flying_team, visit, voucher, voucher_type
 
 urlpatterns = [
     # appointments
@@ -53,8 +53,14 @@ urlpatterns = [
     # worker data
     url(r'^specializations$', worker.specializations, name='web.api.specializations'),
     url(r'^units$', worker.units, name='web.api.units'),
-    url(r'^workers$', worker.workers_for_daily_planning, name='web.api.workers'),
-    url(r'^workers/availabilities$', worker.availabilities, name='web.api.workers.availabilities$'),
+
+    # workers
+    url(r'^workers/(?P<worker_role>[A-z]+)/$', worker.get_workers, name='web.api.workers'),
+
+    # daily planning data
+    url(r'^daily_planning/workers/$', worker.workers_for_daily_planning, name='web.api.workers.daily_planning'),
+    url(r'^daily_planning/workers/availabilities$', worker.availabilities,
+        name='web.api.workers.daily_planning.availabilities$'),
 
     # daily planning events
     url(r'^events/(?P<date>\d{4}-\d{2}-\d{2})/$', daily_planning.events, name='web.api.events'),
@@ -67,4 +73,14 @@ urlpatterns = [
     url(r'^redcap/missing_subjects/(?P<missing_subject_id>\d+):unignore$', redcap.unignore_missing_subject,
         name='web.api.redcap.unignore_missing_subject'),
 
+    # vouchers data
+    url(r'^vouchers/$', voucher.get_vouchers, name='web.api.vouchers'),
+    url(r'^vouchers:columns$', voucher.get_voucher_columns,
+        name='web.api.vouchers.columns'),
+
+    # voucher types data
+    url(r'^voucher_types/$', voucher_type.get_voucher_types, name='web.api.voucher_types'),
+    url(r'^vouchers:columns$', voucher.get_voucher_columns,
+        name='web.api.vouchers.columns'),
+
 ]
diff --git a/smash/web/api_views/voucher.py b/smash/web/api_views/voucher.py
new file mode 100644
index 0000000000000000000000000000000000000000..424bf086c60840e7166d793a4099f97b63ceae3f
--- /dev/null
+++ b/smash/web/api_views/voucher.py
@@ -0,0 +1,137 @@
+import logging
+
+from django.http import JsonResponse
+
+from web.api_views.serialization_utils import get_filters_for_data_table_request, add_column, \
+    serialize_date
+from web.models import Voucher
+
+logger = logging.getLogger(__name__)
+
+
+def get_vouchers_order(vouchers_to_be_ordered, order_column, order_direction):
+    result = vouchers_to_be_ordered
+    if order_direction == "asc":
+        order_direction = ""
+    else:
+        order_direction = "-"
+    if order_column == "first_name":
+        result = vouchers_to_be_ordered.order_by(order_direction + 'study_subject__subject__first_name')
+    elif order_column == "last_name":
+        result = vouchers_to_be_ordered.order_by(order_direction + 'study_subject__subject__last_name')
+    elif order_column == "number":
+        result = vouchers_to_be_ordered.order_by(order_direction + 'number')
+    elif order_column == "expiry_date":
+        result = vouchers_to_be_ordered.order_by(order_direction + 'expiry_date')
+    elif order_column == "issue_date":
+        result = vouchers_to_be_ordered.order_by(order_direction + 'issue_date')
+    elif order_column == "id":
+        result = vouchers_to_be_ordered.order_by(order_direction + 'id')
+    elif order_column == "type":
+        result = vouchers_to_be_ordered.order_by(order_direction + 'voucher_type__code')
+    elif order_column == "status":
+        result = vouchers_to_be_ordered.order_by(order_direction + 'status')
+    else:
+        logger.warn("Unknown sort column: " + str(order_column))
+    return result
+
+
+def get_vouchers_filtered(vouchers_to_be_filtered, filters):
+    result = vouchers_to_be_filtered
+    for row in filters:
+        column = row[0]
+        value = row[1]
+        if column == "first_name":
+            result = result.filter(study_subject__subject__first_name__icontains=value)
+        elif column == "last_name":
+            result = result.filter(study_subject__subject__last_name__icontains=value)
+        elif column == "number":
+            result = result.filter(number__icontains=value)
+        elif column == "type":
+            result = result.filter(voucher_type=value)
+        elif column == "status":
+            result = result.filter(status=value)
+        elif column == "voucher_partner":
+            result = result.filter(usage_partner=value)
+        elif column == "feedback":
+            result = result.filter(feedback__icontains=value)
+        elif column == "":
+            pass
+        else:
+            message = "UNKNOWN filter: "
+            if column is None:
+                message += "[None]"
+            else:
+                message += str(column)
+            logger.warn(message)
+    return result
+
+
+# noinspection PyUnusedLocal
+def get_voucher_columns(request):
+    result = []
+    add_column(result, "First name", "first_name", None, "string_filter")
+    add_column(result, "Last name", "last_name", None, "string_filter")
+    add_column(result, "Number", "number", None, "string_filter")
+    add_column(result, "Type", "type", None, "voucher_type_filter")
+    add_column(result, "Status", "status", None, "voucher_status_filter")
+    add_column(result, "Voucher partner", "voucher_partner", None, "voucher_partner_filter")
+    add_column(result, "Issue date", "issue_date", None, None)
+    add_column(result, "Expiry date", "expiry_date", None, None)
+    add_column(result, "Edit", "edit", None, None, sortable=False)
+
+    return JsonResponse({"columns": result})
+
+
+def get_vouchers(request):
+    # id of the query from dataTable: https://datatables.net/manual/server-side
+    draw = int(request.GET.get("draw", "-1"))
+
+    start = int(request.GET.get("start", "0"))
+    length = int(request.GET.get("length", "10"))
+    order = int(request.GET.get("order[0][column]", "0"))
+    order_dir = request.GET.get("order[0][dir]", "asc")
+    order_column = request.GET.get("columns[" + str(order) + "][data]", "last_name")
+
+    filters = get_filters_for_data_table_request(request)
+
+    all_vouchers = Voucher.objects.all()
+
+    count = all_vouchers.count()
+
+    ordered_vouchers = get_vouchers_order(all_vouchers, order_column, order_dir)
+    filtered_vouchers = get_vouchers_filtered(ordered_vouchers, filters)
+    sliced_vouchers = filtered_vouchers[start:(start + length)]
+
+    result_vouchers = sliced_vouchers
+
+    count_filtered = filtered_vouchers.count()
+
+    data = []
+    for voucher in result_vouchers:
+        data.append(serialize_voucher(voucher))
+
+    return JsonResponse({
+        "draw": draw,
+        "recordsTotal": count,
+        "recordsFiltered": count_filtered,
+        "data": data,
+    })
+
+
+def serialize_voucher(voucher):
+    issue_date = serialize_date(voucher.issue_date)
+    expiry_date = serialize_date(voucher.expiry_date)
+    result = {
+        "first_name": voucher.study_subject.subject.first_name,
+        "last_name": voucher.study_subject.subject.last_name,
+        "issue_date": issue_date,
+        "type": voucher.voucher_type.code,
+        "status": str(voucher.status),
+        "voucher_partner": str(voucher.usage_partner),
+        "feedback": voucher.feedback,
+        "expiry_date": expiry_date,
+        "number": voucher.number,
+        "id": voucher.id,
+    }
+    return result
diff --git a/smash/web/api_views/voucher_type.py b/smash/web/api_views/voucher_type.py
new file mode 100644
index 0000000000000000000000000000000000000000..8cf1c18f3ad80b8fa1bec409f9194d95d4f54823
--- /dev/null
+++ b/smash/web/api_views/voucher_type.py
@@ -0,0 +1,32 @@
+import logging
+
+from django.http import JsonResponse
+
+from web.models import VoucherType
+
+logger = logging.getLogger(__name__)
+
+
+def get_voucher_types(request):
+    all_vouchers = VoucherType.objects.all()
+
+    count = all_vouchers.count()
+
+    data = []
+    for voucher_type in all_vouchers:
+        data.append(serialize_voucher_type(voucher_type))
+
+    return JsonResponse({
+        "recordsTotal": count,
+        "recordsFiltered": count,
+        "data": data,
+    })
+
+
+def serialize_voucher_type(voucher_type):
+    result = {
+        "code": voucher_type.code,
+        "description": voucher_type.description,
+        "id": voucher_type.id,
+    }
+    return result
diff --git a/smash/web/api_views/worker.py b/smash/web/api_views/worker.py
index bdf249541b8f3ac637b1ca4b35e332806cc7c782..881f15287d969ffaea7a1fb6b58fdb1f23ca78ca 100644
--- a/smash/web/api_views/worker.py
+++ b/smash/web/api_views/worker.py
@@ -61,3 +61,27 @@ def availabilities(request):
     return JsonResponse({
         "availabilities": result,
     })
+
+
+def get_workers(request, worker_role):
+    all_workers = Worker.get_workers_by_worker_type(worker_role).distinct()
+    count = all_workers.count()
+
+    data = []
+    for voucher_type in all_workers:
+        data.append(serialize_worker(voucher_type))
+
+    return JsonResponse({
+        "recordsTotal": count,
+        "recordsFiltered": count,
+        "data": data,
+    })
+
+
+def serialize_worker(worker):
+    result = {
+        "first_name": worker.first_name,
+        "last_name": worker.last_name,
+        "id": worker.id,
+    }
+    return result
diff --git a/smash/web/docx_helper.py b/smash/web/docx_helper.py
index 49c2fc87105b7722a9ab341dd37f4b90fe67242b..a35bfc83043098f6f13b1a6e3d66980d76aebda4 100644
--- a/smash/web/docx_helper.py
+++ b/smash/web/docx_helper.py
@@ -19,3 +19,19 @@ def process_file(path_to_docx, path_to_new_docx, changes_to_apply):
                 paragraph.text = paragraph.text.replace(placeholder, replacement)
 
     doc.save(path_to_new_docx)
+
+
+def merge_files(files, path_to_new_docx):
+    merged_document = Document()
+
+    for index, input_file in enumerate(files):
+        sub_doc = Document(input_file)
+
+        # Don't add a page break if you've reached the last file.
+        if index < len(files) - 1:
+            sub_doc.add_page_break()
+
+        for element in sub_doc.element.body:
+            merged_document.element.body.append(element)
+
+    merged_document.save(path_to_new_docx)
diff --git a/smash/web/migrations/0112_auto_20180604_1021.py b/smash/web/migrations/0112_auto_20180604_1021.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d99ccd90a40da44049780bf713e00f2267f905c
--- /dev/null
+++ b/smash/web/migrations/0112_auto_20180604_1021.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2018-06-04 10:21
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('web', '0111_auto_20180601_1318'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='mailtemplate',
+            name='context',
+            field=models.CharField(choices=[(b'A', b'Appointment'), (b'S', b'Subject'), (b'V', b'Visit'), (b'C', b'Voucher')], max_length=1),
+        ),
+        migrations.AlterField(
+            model_name='mailtemplate',
+            name='language',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='web.Language'),
+        ),
+    ]
diff --git a/smash/web/models/constants.py b/smash/web/models/constants.py
index 0ef157f4b99f0f009eec4f5de0cca8f20a651305..865f10ccf3c1c1e17f102a0cdca273c34cdc1014 100644
--- a/smash/web/models/constants.py
+++ b/smash/web/models/constants.py
@@ -48,14 +48,18 @@ KIT_EMAIL_DAY_OF_WEEK_CONFIGURATION_TYPE = "KIT_EMAIL_DAY_OF_WEEK_CONFIGURATION_
 MAIL_TEMPLATE_CONTEXT_SUBJECT = 'S'
 MAIL_TEMPLATE_CONTEXT_APPOINTMENT = 'A'
 MAIL_TEMPLATE_CONTEXT_VISIT = 'V'
+MAIL_TEMPLATE_CONTEXT_VOUCHER = 'C'
 
 MAIL_TEMPLATE_CONTEXT_CHOICES = (
     (MAIL_TEMPLATE_CONTEXT_APPOINTMENT, 'Appointment'),
     (MAIL_TEMPLATE_CONTEXT_SUBJECT, 'Subject'),
     (MAIL_TEMPLATE_CONTEXT_VISIT, 'Visit'),
+    (MAIL_TEMPLATE_CONTEXT_VOUCHER, 'Voucher'),
 )
 LOCALE_CHOICES = [(value, value) for value in sorted(locale.windows_locale.values())]
 
+DEFAULT_LOCALE_NAME = "fr_FR"
+
 MONDAY_AS_DAY_OF_WEEK = 1
 TUESDAY_AS_DAY_OF_WEEK = 2
 WEDNESDAY_AS_DAY_OF_WEEK = 3
diff --git a/smash/web/models/language.py b/smash/web/models/language.py
index 8db4cd2733ebc47242c4bad1e82408e11b45f1b1..5848ace6180745d33749f3140e618803dffcce02 100644
--- a/smash/web/models/language.py
+++ b/smash/web/models/language.py
@@ -2,7 +2,7 @@
 
 from django.db import models
 
-from .constants import LOCALE_CHOICES
+from .constants import LOCALE_CHOICES, DEFAULT_LOCALE_NAME
 
 
 class Language(models.Model):
@@ -13,7 +13,8 @@ class Language(models.Model):
     name = models.CharField(max_length=20)
     image = models.ImageField()
     order = models.IntegerField(default=0)
-    locale = models.CharField(max_length=10, choices=LOCALE_CHOICES, null=False, blank=False, default="fr_FR")
+    locale = models.CharField(max_length=10, choices=LOCALE_CHOICES, null=False, blank=False,
+                              default=DEFAULT_LOCALE_NAME)
     windows_locale_name = models.CharField(max_length=10, choices=LOCALE_CHOICES, null=False, blank=False,
                                            default="French")
 
diff --git a/smash/web/models/mail_template.py b/smash/web/models/mail_template.py
index d4f72d1867a4de358b55957fbf9cb3686f40b960..2089ae1ecf52516051ec5f189ec78ae60576eca3 100644
--- a/smash/web/models/mail_template.py
+++ b/smash/web/models/mail_template.py
@@ -7,9 +7,9 @@ from contextlib import contextmanager
 from django.db import models
 
 from .constants import MAIL_TEMPLATE_CONTEXT_CHOICES, MAIL_TEMPLATE_CONTEXT_APPOINTMENT, \
-    MAIL_TEMPLATE_CONTEXT_SUBJECT, MAIL_TEMPLATE_CONTEXT_VISIT
+    MAIL_TEMPLATE_CONTEXT_SUBJECT, MAIL_TEMPLATE_CONTEXT_VISIT, MAIL_TEMPLATE_CONTEXT_VOUCHER, DEFAULT_LOCALE_NAME
 from ..docx_helper import process_file
-from ..models import Appointment, Visit, StudySubject, Worker
+from ..models import Appointment, Visit, StudySubject, Worker, Voucher
 
 DATE_FORMAT_FULL = "%A %d %B %Y"
 
@@ -99,9 +99,26 @@ class MailTemplate(models.Model):
         ("##A_TYPES##", "Appointment's types", "comma separated"),
     ]
 
+    MAILS_TEMPLATE_VOUCHER_TAGS = [
+        ("##C_NUMBER##", "Number", ''),
+        ("##C_PATIENT_NAME##", "Voucher Partner name", ''),
+        ("##C_VOUCHER_TYPE##", "Voucher type", ''),
+
+        ("##C_ISSUE_DATE_SHORT##", "Issue date", get_formatted_time(DATE_FORMAT_SHORT)),
+        ("##C_EXPIRY_START_SHORT##", "Expiry date", get_formatted_time(DATE_FORMAT_SHORT)),
+
+        ("##C_PARTNER_NAME##", "Voucher Partner name", ''),
+        ("##C_PARTNER_ADDRESS##", "Voucher Partner address", ''),
+        ("##C_PARTNER_CITY##", "Voucher Partner city", ''),
+        ("##C_PARTNER_POSTAL_CODE##", "Voucher Partner postal code", ''),
+        ("##C_PARTNER_COUNTRY##", "Voucher Partner country", ''),
+        ("##C_PARTNER_PHONE##", "Voucher Partner phone", ''),
+        ("##C_HOURS##", "Hours", ''),
+    ]
+
     name = models.CharField(max_length=255)
     context = models.CharField(max_length=1, choices=MAIL_TEMPLATE_CONTEXT_CHOICES)
-    language = models.ForeignKey("web.Language", on_delete=models.CASCADE)
+    language = models.ForeignKey("web.Language", on_delete=models.CASCADE, null=True)
     template_file = models.FileField(upload_to='templates/')
 
     @staticmethod
@@ -112,6 +129,10 @@ class MailTemplate(models.Model):
     def get_subject_mail_templates(languages):
         return MailTemplate.get_mail_templates_for_context(languages, MAIL_TEMPLATE_CONTEXT_SUBJECT)
 
+    @staticmethod
+    def get_voucher_mail_templates(languages):
+        return MailTemplate.get_mail_templates_for_context(languages, MAIL_TEMPLATE_CONTEXT_VOUCHER)
+
     @staticmethod
     def get_visit_mail_templates(languages):
         return MailTemplate.get_mail_templates_for_context(languages, MAIL_TEMPLATE_CONTEXT_VISIT)
@@ -123,17 +144,23 @@ class MailTemplate(models.Model):
         active_templates = []
         disabled_templates = []
         for template in templates:
-            if template.language.name in languages_names:
+            if template.language is None:
+                if len(languages) == 0:
+                    active_templates.append(template)
+                else:
+                    disabled_templates.append(template)
+            elif template.language.name in languages_names:
                 active_templates.append(template)
             else:
                 disabled_templates.append(template)
-        active_templates.sort(key=lambda x: languages_names.index(x.language.name))
+        active_templates.sort(key=lambda x: languages_names.index(x.language.name) if x.language is not None else -1)
         return active_templates, disabled_templates
 
     def apply(self, instance, user, stream):
         appointment = None
         visit = None
         study_subject = None
+        voucher = None
         if isinstance(instance, Appointment):
             appointment = instance
             visit = instance.visit
@@ -143,19 +170,29 @@ class MailTemplate(models.Model):
             study_subject = visit.subject
         elif isinstance(instance, StudySubject):
             study_subject = instance
+        elif isinstance(instance, Voucher):
+            voucher = instance
         # set locale to get correct date format
-        locale_name = self.language.locale
-        if platform.system() == 'Windows':
-            locale_name = self.language.windows_locale_name
+        locale_name = self.get_locale_name()
         with setlocale(locale_name.encode('utf8')):
             replacements = {}
             self._add_generic_replacements(replacements, Worker.get_by_user(user))
             self._add_appointment_replacements(replacements, appointment)
             self._add_visit_replacements(replacements, visit)
             self._add_subject_replacements(replacements, study_subject)
+            self._add_voucher_replacements(replacements, voucher)
             process_file(self.template_file.path, stream, replacements)
         return stream
 
+    def get_locale_name(self):
+        if self.language is None:
+            locale_name = DEFAULT_LOCALE_NAME
+        else:
+            locale_name = self.language.locale
+            if platform.system() == 'Windows':
+                locale_name = self.language.windows_locale_name
+        return locale_name
+
     def _add_generic_replacements(self, replacements, worker):
         current_datetime = datetime.datetime.now()
         replacements.update({
@@ -237,3 +274,21 @@ class MailTemplate(models.Model):
                 '##S_MAIL_LANGUAGE##': str(study_subject.subject.default_written_communication_language),
                 '##S_KNOWN_LANGUAGES##': ", ".join([l.name for l in study_subject.subject.languages.all()])
             })
+
+    def _add_voucher_replacements(self, replacements, voucher):
+        if voucher is not None:
+            replacements.update({
+                "##C_NUMBER##": voucher.number,
+                "##C_PATIENT_NAME##": voucher.study_subject.subject.first_name + ' ' + voucher.study_subject.subject.last_name,
+                "##C_VOUCHER_TYPE##": voucher.voucher_type.description,
+                "##C_ISSUE_DATE_SHORT##": voucher.issue_date.strftime(DATE_FORMAT_SHORT).decode(date_format_encoding()),
+                "##C_EXPIRY_START_SHORT##": voucher.expiry_date.strftime(DATE_FORMAT_SHORT).decode(
+                    date_format_encoding()),
+                "##C_PARTNER_NAME##": voucher.usage_partner.first_name + ' ' + voucher.usage_partner.last_name,
+                "##C_PARTNER_ADDRESS##": voucher.usage_partner.address,
+                "##C_PARTNER_POSTAL_CODE##": voucher.usage_partner.postal_code,
+                "##C_PARTNER_CITY##": voucher.usage_partner.city,
+                "##C_PARTNER_COUNTRY##": unicode(voucher.usage_partner.country),
+                "##C_PARTNER_PHONE##": voucher.usage_partner.phone_number,
+                "##C_HOURS##": str(voucher.hours),
+            })
diff --git a/smash/web/static/js/smash.js b/smash/web/static/js/smash.js
index 6da68884c152b60a3e56823dc4e808d4b604e76c..45122991ee81b1341c86e6e665f1faf113e255d5 100644
--- a/smash/web/static/js/smash.js
+++ b/smash/web/static/js/smash.js
@@ -215,6 +215,8 @@ function createTable(params) {
     var flying_teams_url = params.flying_teams_url;
     var appointment_types_url = params.appointment_types_url;
     var subjects_url = params.subjects_url;
+    var voucher_types_url = params.voucher_types_url;
+    var voucher_partner_url = params.voucher_partner_url;
     var columnsDefinition = params.columns;
 
     tableElement.appendChild(createHeader(columnsDefinition));
@@ -239,6 +241,15 @@ function createTable(params) {
         $(this).html('<select style="width:60px" >' + options + '</select>');
     });
 
+    $(tableElement).find('tfoot div[name="voucher_status_filter"]').each(function () {
+        $(this).html('<select style="width:60px" ><option value selected="selected">---</option>' +
+            '<option value="NEW">NEW</option>' +
+            '<option value="IN_USE">IN USE</option>' +
+            '<option value="USED">USED</option>' +
+            '<option value="EXPIRED">EXPIRED</option>' +
+            '</select>');
+    });
+
     $(tableElement).find('tfoot div[name="visit_filter"]').each(function () {
         $(this).html('<select style="width:60px" >' +
             '<option value selected="selected">---</option>' +
@@ -265,6 +276,29 @@ function createTable(params) {
         });
     });
 
+    $(tableElement).find('tfoot div[name="voucher_type_filter"]').each(function () {
+        var obj = $(this);
+        obj.html('<select style="width:80px"><option value selected="selected">---</option></select>');
+        var select = $('select', obj);
+        $.get(voucher_types_url, function (content) {
+            $.each(content.data, function (index, voucher_type) {
+                select.append('<option value="' + voucher_type.id + '">' + voucher_type.code + '</option>');
+            });
+        });
+    });
+
+    $(tableElement).find('tfoot div[name="voucher_partner_filter"]').each(function () {
+        var obj = $(this);
+        obj.html('<select style="width:80px"><option value selected="selected">---</option></select>');
+        var select = $('select', obj);
+        $.get(voucher_partner_url, function (content) {
+            $.each(content.data, function (index, voucher_partner) {
+                select.append('<option value="' + voucher_partner.id + '">' + voucher_partner.first_name + ' ' +
+                    voucher_partner.last_name + ' ' + '</option>');
+            });
+        });
+    });
+
     $(tableElement).find('tfoot div[name="flying_team_filter"]').each(function () {
         var obj = $(this);
         obj.html('<select style="width:80px"><option value selected="selected">---</option></select>');
diff --git a/smash/web/templates/appointments/index.html b/smash/web/templates/appointments/index.html
index f7edcc999350542ff409a3ed620a3f6982a9fdaa..ee4b4f0c9bef0184c7192368d76fd369713f1f6f 100644
--- a/smash/web/templates/appointments/index.html
+++ b/smash/web/templates/appointments/index.html
@@ -157,7 +157,7 @@
                 events: get_calendar_events_function(
                     "{% url 'web.api.appointments' full_list %}",
                     true, dayHeaders,
-                    "{% url 'web.api.workers.availabilities$' %}")
+                    "{% url 'web.api.workers.daily_planning.availabilities$' %}")
             });
         });
     </script>
diff --git a/smash/web/templates/daily_planning.html b/smash/web/templates/daily_planning.html
index 6054dcf172570251632ad338f0010d9e61355803..c02b5d7d6e7e9262b53a20f90b1eb9c4d4a261d9 100644
--- a/smash/web/templates/daily_planning.html
+++ b/smash/web/templates/daily_planning.html
@@ -46,7 +46,7 @@
     <script src="{% static 'fullcalendar-scheduler/lib/fullcalendar.min.js' %}"></script>
     <script src="{% static 'fullcalendar-scheduler/scheduler.min.js' %}"></script>
     <script>
-        var resources_url = '{% url 'web.api.workers' %}';
+        var resources_url = '{% url 'web.api.workers.daily_planning' %}';
         var events_url = '{% url 'web.api.events_persist' %}';
     </script>
     {% include "includes/datepicker.js.html" %}
diff --git a/smash/web/templates/includes/subject_vouchers_box.html b/smash/web/templates/includes/subject_vouchers_box.html
index a17610a2a9e70bf1ff3d106b2a57eeeca09c3211..4cd7c2f4c88d48d3887432691a20cb687721d284 100644
--- a/smash/web/templates/includes/subject_vouchers_box.html
+++ b/smash/web/templates/includes/subject_vouchers_box.html
@@ -13,6 +13,7 @@
                     <thead>
                     <tr>
 
+                        <th class="text-center">Select</th>
                         <th class="text-center">Number</th>
                         <th class="text-center">Type</th>
                         <th class="text-center">Issue date</th>
@@ -26,6 +27,7 @@
                     <tbody>
                     {% for voucher in subject.vouchers.all %}
                         <tr>
+                            <td><input type="checkbox" data-id="{{ voucher.id }}" class="voucher_checkbox"/></td>
                             <td>{{ voucher.number }}</td>
                             <td>{{ voucher.voucher_type }}</td>
                             <td>{{ voucher.issue_date }}</td>
@@ -39,6 +41,18 @@
                     {% endfor %}
                     </tbody>
                 </table>
+                <a href="#" data-url="{% url "web.views.mail_template_generate_for_vouchers" %}" onclick='
+                var checkboxes = $(".voucher_checkbox");
+                var url = $(this).data("url")+"?voucher_id=";
+
+                for (var i=0;i<checkboxes.length;i++){
+                    if ($(checkboxes[i]).is(":checked")) {
+                      url+=$(checkboxes[i]).data("id")+",";
+                    }
+                }
+                this.href = url;
+                '>Print vouchers</a>
+
             </div>
         </div>
 
diff --git a/smash/web/templates/subjects/edit.html b/smash/web/templates/subjects/edit.html
index 669f863086174f60aaca05907bf6713c84725340..73249d4fb51632bdc70a331f60e4dc903927ce05 100644
--- a/smash/web/templates/subjects/edit.html
+++ b/smash/web/templates/subjects/edit.html
@@ -109,10 +109,6 @@
             </div><!-- /.col-md-12 -->
         </div><!-- /.row -->
 
-        {% if study_subject.study.columns.vouchers %}
-            {% include 'includes/subject_vouchers_box.html' with subject=study_subject %}
-        {% endif %}
-
         {% include 'includes/mail_templates_box.html' with instance_id=study_subject.id %}
 
         {% include 'includes/contact_attempts_box.html' with subject=study_subject contact_attempts=contact_attempts %}
@@ -157,6 +153,10 @@
             </div><!-- /.modal-dialog -->
         </div><!-- /.modal -->
 
+        {% if study_subject.study.columns.vouchers %}
+            {% include 'includes/subject_vouchers_box.html' with subject=study_subject %}
+        {% endif %}
+
     {% endblock %}
 
 
diff --git a/smash/web/templates/vouchers/add_edit.html b/smash/web/templates/vouchers/add_edit.html
index 488467ec25e1bd6a4184b4e60e82ea118bf6a92f..c0083e92cc23d2b666a8a60547a0237e04123da5 100644
--- a/smash/web/templates/vouchers/add_edit.html
+++ b/smash/web/templates/vouchers/add_edit.html
@@ -90,6 +90,8 @@
                             </table>
                         </div>
 
+                        {% include 'includes/mail_templates_box.html' with instance_id=voucher.id %}
+
                     {% endif %}
                 </div>
             </div>
diff --git a/smash/web/templates/vouchers/list.html b/smash/web/templates/vouchers/list.html
index 1b29b2965c072b85dd28d1342adb9417809791fa..99a39340aec7ed9bd833dd733ee3b5bac9659a89 100644
--- a/smash/web/templates/vouchers/list.html
+++ b/smash/web/templates/vouchers/list.html
@@ -18,39 +18,12 @@
 {% block maincontent %}
 
     <div class="box-body">
-        <table id="table" class="table table-bordered table-striped">
-            <thead>
-            <tr>
-                <th>Number</th>
-                <th>Type</th>
-                <th>First name</th>
-                <th>Last name</th>
-                <th>Issue date</th>
-                <th>Expiry date</th>
-                <th>Status</th>
-                <th>Partner</th>
-                <th>Feedback</th>
-                <th>Edit</th>
-            </tr>
-            </thead>
-            <tbody>
-            {% for voucher in vouchers %}
-                <tr>
-                    <td>{{ voucher.number }}</td>
-                    <td>{{ voucher.voucher_type }}</td>
-                    <td>{{ voucher.study_subject.subject.first_name }}</td>
-                    <td>{{ voucher.study_subject.subject.last_name }}</td>
-                    <td>{{ voucher.issue_date }}</td>
-                    <td>{{ voucher.expiry_date }}</td>
-                    <td>{{ voucher.status }}</td>
-                    <td>{{ voucher.usage_partner.first_name }} {{ voucher.usage_partner.last_name }}</td>
-                    <td>{{ voucher.feedback }}</td>
-                    <td><a href="{% url 'web.views.voucher_edit' voucher.id %}"><i class="fa fa-edit"></i></a></td>
-                </tr>
-            {% endfor %}
-            </tbody>
+        <table id="table" class="table table-bordered table-striped table-responsive">
         </table>
     </div>
+    <h3>Visible columns</h3>
+    <div id="visible-column-checkboxes" style="display:table; width:100%">
+    </div>
 {% endblock maincontent %}
 
 {% block scripts %}
@@ -58,17 +31,27 @@
 
     <script src="{% static 'AdminLTE/plugins/datatables/jquery.dataTables.min.js' %}"></script>
     <script src="{% static 'AdminLTE/plugins/datatables/dataTables.bootstrap.min.js' %}"></script>
+    <script src="{% static 'js/appointment.js' %}"></script>
 
     <script>
-        $(function () {
-            $('#table').DataTable({
-                "paging": true,
-                "lengthChange": false,
-                "searching": true,
-                "ordering": true,
-                "info": true,
-                "autoWidth": false
-            });
+        function getVoucherEditUrl(id) {
+            return "{% url 'web.views.voucher_edit' 1234567 %}".replace(/1234567/, id);
+        }
+
+        $.get("{% url 'web.api.vouchers.columns' %}", function (data) {
+            createAppointmentsTable({
+                appointment_types_url: "{% url 'web.api.appointment_types' %}",
+                subject_types_url: "{% url 'web.api.subject_types' %}",
+                locations_url: "{% url 'web.api.locations' %}",
+                subjects_url: "{% url 'web.api.vouchers'%}",
+                voucher_partner_url: "{% url 'web.api.workers' 'VOUCHER_PARTNER' %}",
+                voucher_types_url: "{% url 'web.api.voucher_types' %}",
+                flying_teams_url: "{% url 'web.api.flying_teams' %}",
+                tableElement: document.getElementById("table"),
+                columns: getColumns(data.columns, getVoucherEditUrl),
+                checkboxesElement: document.getElementById("visible-column-checkboxes")
+            })
         });
+
     </script>
 {% endblock scripts %}
diff --git a/smash/web/tests/api_views/test_voucher.py b/smash/web/tests/api_views/test_voucher.py
new file mode 100644
index 0000000000000000000000000000000000000000..adbb1fd521058559ef751e5d77b7b521037e2901
--- /dev/null
+++ b/smash/web/tests/api_views/test_voucher.py
@@ -0,0 +1,204 @@
+# coding=utf-8
+import datetime
+import json
+import logging
+
+from django.urls import reverse
+
+from web.api_views.voucher import get_vouchers_filtered, get_vouchers_order
+from web.models import Voucher
+from web.models.constants import VOUCHER_STATUS_USED, VOUCHER_STATUS_NEW
+from web.tests import LoggedInWithWorkerTestCase
+from web.tests.functions import create_get_suffix, create_voucher
+from web.views.notifications import get_today_midnight_date
+
+logger = logging.getLogger(__name__)
+
+
+class TestVoucherApi(LoggedInWithWorkerTestCase):
+    def setUp(self):
+        super(TestVoucherApi, self).setUp()
+        self.voucher = create_voucher()
+
+    def test_vouchers_general(self):
+        response = self.client.get(reverse('web.api.vouchers'))
+        self.assertEqual(response.status_code, 200)
+
+    def test_vouchers_general_search(self):
+        name = "Piotrek"
+        self.voucher.study_subject.subject.first_name = name
+        self.voucher.study_subject.subject.save()
+
+        params = {
+            "columns[0][search][value]": "another_name",
+            "columns[0][data]": "first_name"
+        }
+        url = ("%s" + create_get_suffix(params)) % reverse('web.api.vouchers')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+        self.assertFalse(name in response.content)
+
+        params["columns[0][search][value]"] = name
+        url = ("%s" + create_get_suffix(params)) % reverse('web.api.vouchers')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+        self.assertTrue(name in response.content)
+
+    def check_voucher_filtered(self, filters, result):
+        vouchers = get_vouchers_filtered(Voucher.objects.all(), filters)
+        self.assertEqual(len(result), vouchers.count())
+        for index in range(len(result)):
+            self.assertEqual(result[index], vouchers[index])
+
+    def check_voucher_ordered(self, order, result):
+        vouchers = get_vouchers_order(Voucher.objects.all(), order, "asc")
+        self.assertEqual(len(result), vouchers.count())
+        for index in range(len(result)):
+            self.assertEqual(result[index], vouchers[index])
+
+        vouchers = get_vouchers_order(Voucher.objects.all(), order, "desc")
+        length = len(result)
+        self.assertEqual(length, vouchers.count())
+        for index in range(length):
+            self.assertEqual(result[length - index - 1], vouchers[index])
+
+    def test_vouchers_sort_number(self):
+        voucher = self.voucher
+        voucher.number = "PPP"
+        voucher.save()
+
+        voucher2 = create_voucher()
+        voucher2.number = "QQQ"
+        voucher2.save()
+
+        self.check_voucher_ordered("number", [voucher, voucher2])
+
+    def test_subjects_sort_id(self):
+        voucher = self.voucher
+
+        voucher2 = create_voucher()
+
+        self.check_voucher_ordered("id", [voucher, voucher2])
+
+    def test_vouchers_sort_issue_date(self):
+        voucher = self.voucher
+        voucher.issue_date = get_today_midnight_date()
+        voucher.save()
+
+        voucher2 = create_voucher()
+        voucher2.issue_date = get_today_midnight_date() + datetime.timedelta(days=1)
+        voucher2.save()
+
+        self.check_voucher_ordered("issue_date", [voucher, voucher2])
+
+    def test_vouchers_sort_expiry_date(self):
+        voucher = self.voucher
+        voucher.expiry_date = get_today_midnight_date()
+        voucher.save()
+
+        voucher2 = create_voucher()
+        voucher2.expiry_date = get_today_midnight_date() + datetime.timedelta(days=1)
+        voucher2.save()
+
+        self.check_voucher_ordered("expiry_date", [voucher, voucher2])
+
+    def test_vouchers_sort_type(self):
+        voucher = self.voucher
+        voucher.voucher_type.code = "ZZ"
+        voucher.voucher_type.save()
+
+        voucher2 = create_voucher()
+        voucher2.voucher_type.code = "AA"
+        voucher2.save()
+
+        self.check_voucher_ordered("type", [voucher2, voucher])
+
+    def test_vouchers_sort_status(self):
+        voucher = self.voucher
+        voucher.status = VOUCHER_STATUS_USED
+        voucher.save()
+
+        voucher2 = create_voucher()
+        voucher2.status = VOUCHER_STATUS_NEW
+        voucher2.save()
+
+        self.check_voucher_ordered("status", [voucher2, voucher])
+
+    def test_vouchers_sort_unknown(self):
+        self.check_voucher_ordered("unk", [self.voucher])
+
+    def test_vouchers_filter_nd_number(self):
+        voucher = self.voucher
+        voucher.number = "PPP"
+        voucher.save()
+
+        voucher2 = create_voucher()
+        voucher2.number = "QQQ"
+        voucher2.save()
+
+        self.check_voucher_filtered([["number", "P"]], [voucher])
+
+    def test_vouchers_filter_last_name(self):
+        voucher = self.voucher
+        voucher.study_subject.subject.last_name = "PPP"
+        voucher.study_subject.subject.save()
+
+        create_voucher()
+
+        self.check_voucher_filtered([["last_name", "P"]], [voucher])
+
+    def test_vouchers_filter_type(self):
+        voucher = self.voucher
+
+        create_voucher()
+
+        self.check_voucher_filtered([["type", str(voucher.voucher_type.id)]], [voucher])
+
+    def test_vouchers_filter_status(self):
+        voucher = self.voucher
+        voucher.status = VOUCHER_STATUS_NEW
+        voucher.save()
+
+        voucher2 = create_voucher()
+        voucher2.status = VOUCHER_STATUS_USED
+        voucher2.save()
+
+        self.check_voucher_filtered([["status", VOUCHER_STATUS_NEW]], [voucher])
+
+    def test_vouchers_filter_voucher_partner(self):
+        voucher = self.voucher
+
+        create_voucher()
+
+        self.check_voucher_filtered([["voucher_partner", str(voucher.usage_partner_id)]], [voucher])
+
+    def test_vouchers_filter_feedback(self):
+        voucher = self.voucher
+        voucher.feedback = "XAS"
+        voucher.save()
+
+        create_voucher()
+
+        self.check_voucher_filtered([["feedback", "X"]], [voucher])
+
+    def test_vouchers_filter_unknown(self):
+        voucher = self.voucher
+
+        self.check_voucher_filtered([["unk", "X"]], [voucher])
+
+    def test_vouchers_filter_unknown_2(self):
+        voucher = self.voucher
+
+        self.check_voucher_filtered([[None, "X"]], [voucher])
+
+    def test_vouchers_filter_empty(self):
+        voucher = self.voucher
+
+        self.check_voucher_filtered([["", "X"]], [voucher])
+
+    def test_get_columns(self):
+        response = self.client.get(reverse('web.api.vouchers.columns'))
+        self.assertEqual(response.status_code, 200)
+
+        columns = json.loads(response.content)['columns']
+        self.assertTrue(len(columns) > 0)
diff --git a/smash/web/tests/api_views/test_voucher_type.py b/smash/web/tests/api_views/test_voucher_type.py
new file mode 100644
index 0000000000000000000000000000000000000000..c4f799985562877a22965a43368491a2f2a2dd28
--- /dev/null
+++ b/smash/web/tests/api_views/test_voucher_type.py
@@ -0,0 +1,24 @@
+# coding=utf-8
+import datetime
+import logging
+
+from django.urls import reverse
+
+from web.api_views.voucher import get_vouchers_filtered, get_vouchers_order
+from web.models import Voucher
+from web.models.constants import VOUCHER_STATUS_USED, VOUCHER_STATUS_NEW
+from web.tests import LoggedInWithWorkerTestCase
+from web.tests.functions import create_get_suffix, create_voucher, create_voucher_type
+from web.views.notifications import get_today_midnight_date
+
+logger = logging.getLogger(__name__)
+
+
+class TestVoucherTypeApi(LoggedInWithWorkerTestCase):
+    def setUp(self):
+        super(TestVoucherTypeApi, self).setUp()
+        self.voucher_type = create_voucher_type()
+
+    def test_voucher_types_render(self):
+        response = self.client.get(reverse('web.api.voucher_types'))
+        self.assertEqual(response.status_code, 200)
diff --git a/smash/web/tests/api_views/test_worker.py b/smash/web/tests/api_views/test_worker.py
index 22b8e76eec524117da068ee506cc020ff8bf569f..c146d560acf741436cef1ba93667a994940d8fdb 100644
--- a/smash/web/tests/api_views/test_worker.py
+++ b/smash/web/tests/api_views/test_worker.py
@@ -4,10 +4,12 @@ import json
 from django.test import RequestFactory
 from django.urls import reverse
 
+from web.api_views.worker import availabilities
 from web.models import Availability
 from web.models.constants import TUESDAY_AS_DAY_OF_WEEK
-from web.api_views.worker import availabilities
+from web.models.worker_study_role import WORKER_STAFF
 from web.tests import LoggedInWithWorkerTestCase
+from web.tests.functions import create_voucher_partner
 
 
 class TestWorkerApi(LoggedInWithWorkerTestCase):
@@ -48,10 +50,16 @@ class TestWorkerApi(LoggedInWithWorkerTestCase):
         self.assertTrue(unit_name in units)
 
     def test_workers_for_daily_planning(self):
-        response = self.client.get(reverse('web.api.workers'))
+        response = self.client.get(reverse('web.api.workers.daily_planning'))
         self.assertEqual(response.status_code, 200)
         self.assertTrue(self.worker.first_name in response.content)
 
+    def test_voucher_partners(self):
+        voucher_partner = create_voucher_partner()
+        response = self.client.get(reverse('web.api.workers', kwargs={'worker_role': WORKER_STAFF}))
+        self.assertEqual(response.status_code, 200)
+        self.assertTrue(voucher_partner.first_name in response.content)
+
     def test_empty_availabilities(self):
         factory = RequestFactory()
         request = factory.get('/api/workers?start_date=2017-10-20&end_date=2017-11-20')
diff --git a/smash/web/tests/data/voucher_test.docx b/smash/web/tests/data/voucher_test.docx
new file mode 100644
index 0000000000000000000000000000000000000000..8f263326ba270ab7bfe216c812631225bb1fd105
Binary files /dev/null and b/smash/web/tests/data/voucher_test.docx differ
diff --git a/smash/web/tests/functions.py b/smash/web/tests/functions.py
index f7da4add1364911a0e51a2015cfe53842f754b06..0164230b3ae50aa50a93cc8979135df6c938670b 100644
--- a/smash/web/tests/functions.py
+++ b/smash/web/tests/functions.py
@@ -9,7 +9,7 @@ from web.models import Location, AppointmentType, StudySubject, Worker, Visit, A
     VoucherType, VoucherTypePrice, Voucher, Room, Item, WorkerStudyRole
 from web.models.constants import REDCAP_TOKEN_CONFIGURATION_TYPE, REDCAP_BASE_URL_CONFIGURATION_TYPE, \
     SEX_CHOICES_MALE, SUBJECT_TYPE_CHOICES_CONTROL, CONTACT_TYPES_PHONE, \
-    MONDAY_AS_DAY_OF_WEEK, COUNTRY_AFGHANISTAN_ID, VOUCHER_STATUS_NEW, GLOBAL_STUDY_ID
+    MONDAY_AS_DAY_OF_WEEK, COUNTRY_AFGHANISTAN_ID, VOUCHER_STATUS_NEW, GLOBAL_STUDY_ID, DEFAULT_LOCALE_NAME
 from web.models.worker_study_role import ROLE_CHOICES_DOCTOR, WORKER_VOUCHER_PARTNER
 from web.redcap_connector import RedcapSubject
 from web.views.notifications import get_today_midnight_date
@@ -311,7 +311,7 @@ def create_room(owner='Test owner', city='Test city',
     return room
 
 
-def create_language(name="French", locale="fr_FR"):
+def create_language(name="French", locale=DEFAULT_LOCALE_NAME):
     language = Language(name=name, locale=locale)
     language.save()
     return language
diff --git a/smash/web/tests/models/test_mail_template.py b/smash/web/tests/models/test_mail_template.py
index 49c84a13962049a563cd007c6a2b819575e33361..500b3a0709ffb1afdb53a0c13b15cdfa71343c96 100644
--- a/smash/web/tests/models/test_mail_template.py
+++ b/smash/web/tests/models/test_mail_template.py
@@ -5,11 +5,11 @@ from docx import Document
 
 from web.models import MailTemplate
 from web.models.constants import MAIL_TEMPLATE_CONTEXT_APPOINTMENT, MAIL_TEMPLATE_CONTEXT_VISIT, \
-    MAIL_TEMPLATE_CONTEXT_SUBJECT
+    MAIL_TEMPLATE_CONTEXT_SUBJECT, MAIL_TEMPLATE_CONTEXT_VOUCHER
 from web.models.mail_template import DATE_FORMAT_SHORT
 from web.tests.functions import create_language, get_resource_path, create_appointment, create_user, \
     create_study_subject, \
-    create_visit
+    create_visit, create_voucher
 
 
 class MailTemplateModelTests(TestCase):
@@ -34,6 +34,11 @@ class MailTemplateModelTests(TestCase):
         function_to_test = MailTemplate.get_subject_mail_templates
         self.check_get_mail_templates(context, function_to_test)
 
+    def test_get_voucher_mail_templates(self):
+        context = MAIL_TEMPLATE_CONTEXT_VOUCHER
+        function_to_test = MailTemplate.get_voucher_mail_templates
+        self.check_get_mail_templates(context, function_to_test)
+
     def check_get_mail_templates(self, context, function_to_test):
         # create french template
         template_name_french = "test_fr"
@@ -93,6 +98,45 @@ class MailTemplateModelTests(TestCase):
         self.check_doc_contains(doc, [worker_name, str(subject), str(subject.subject.country), subject.nd_number,
                                       subject.get_type_display()])
 
+    def test_apply_voucher(self):
+        template_name_french = "test_without_language"
+        subject = create_study_subject()
+        subject_template_french = MailTemplate(name=template_name_french, language=None,
+                                               context=MAIL_TEMPLATE_CONTEXT_VOUCHER,
+                                               template_file="voucher_test.docx")
+        voucher = create_voucher(study_subject=subject)
+        stream = StringIO.StringIO()
+        subject_template_french.apply(voucher, self.user, stream)
+        doc = Document(stream)
+        worker_name = str(self.user.worker)
+
+        self.check_doc_contains(doc, [worker_name, str(subject), voucher.number, voucher.usage_partner.address,
+                                      voucher.expiry_date.strftime(DATE_FORMAT_SHORT),
+                                      voucher.issue_date.strftime(DATE_FORMAT_SHORT)
+                                      ])
+
+    def test_get_mail_templates_for_context_without_language(self):
+        template_name_french = "test_without_language"
+        MailTemplate(name=template_name_french, language=None,
+                     context=MAIL_TEMPLATE_CONTEXT_VOUCHER,
+                     template_file="voucher_test.docx").save()
+
+        templates = MailTemplate.get_mail_templates_for_context([], context=MAIL_TEMPLATE_CONTEXT_VOUCHER)
+        self.assertEquals(1, len(templates[0]))
+
+        templates = MailTemplate.get_mail_templates_for_context([self.english_language],
+                                                                context=MAIL_TEMPLATE_CONTEXT_VOUCHER)
+        self.assertEquals(0, len(templates[0]))
+
+    def test_get_mail_templates_for_context_without_language_2(self):
+        template_name_french = "test_without_language"
+        MailTemplate(name=template_name_french, language=self.english_language,
+                     context=MAIL_TEMPLATE_CONTEXT_VOUCHER,
+                     template_file="voucher_test.docx").save()
+
+        templates = MailTemplate.get_mail_templates_for_context([], context=MAIL_TEMPLATE_CONTEXT_VOUCHER)
+        self.assertEquals(0, len(templates[0]))
+
     def test_apply_visit(self):
         template_name_french = "test_fr"
         visit = create_visit()
diff --git a/smash/web/tests/test_process_file.py b/smash/web/tests/test_process_file.py
index 61b32c416309e0440feb08020853b6545727dbb6..c2d428d8b822b484fc54631d9f3a921c55c2ae09 100644
--- a/smash/web/tests/test_process_file.py
+++ b/smash/web/tests/test_process_file.py
@@ -9,7 +9,7 @@ from django.test import TestCase
 
 from web.tests.functions import get_resource_path
 from web.models.mail_template import date_format_encoding
-from web.docx_helper import process_file
+from web.docx_helper import process_file, merge_files
 
 logger = logging.getLogger(__name__)
 
@@ -35,3 +35,13 @@ class TestDocxProcessor(TestCase):
         process_file(template_path, output_path, changes)
         self.assertTrue(os.path.isfile(output_path))
         os.remove(output_path)
+
+    def test_merge_files(self):
+        template_path = get_resource_path('template.docx')
+        template_path_2 = get_resource_path('voucher_test.docx')
+
+        output_path = tempfile.mktemp()
+
+        merge_files([template_path, template_path_2], output_path)
+        self.assertTrue(os.path.isfile(output_path))
+        os.remove(output_path)
diff --git a/smash/web/tests/view/test_mail.py b/smash/web/tests/view/test_mail.py
new file mode 100644
index 0000000000000000000000000000000000000000..900c84a7bcadf38a37c420308d638eb4f2c74d14
--- /dev/null
+++ b/smash/web/tests/view/test_mail.py
@@ -0,0 +1,22 @@
+import logging
+
+from django.urls import reverse
+
+from web.models import MailTemplate
+from web.models.constants import MAIL_TEMPLATE_CONTEXT_VOUCHER
+from web.tests.functions import create_voucher, get_resource_path
+from web.tests import LoggedInTestCase
+
+logger = logging.getLogger(__name__)
+
+
+class MailTests(LoggedInTestCase):
+    def test_generate_vouchers(self):
+        voucher = create_voucher()
+        MailTemplate(name="name", language=None,
+                     context=MAIL_TEMPLATE_CONTEXT_VOUCHER,
+                     template_file=get_resource_path('upcoming_appointment_FR.docx')).save()
+
+        page = reverse('web.views.mail_template_generate_for_vouchers') + "?voucher_id=" + str(voucher.id)
+        response = self.client.get(page)
+        self.assertEqual(response.status_code, 200)
diff --git a/smash/web/urls.py b/smash/web/urls.py
index 63496dee167e3951c5c7957bdc0e8c9888609c0d..ff3f13a912ccad5c56f589b19fefa808f8d0c49f 100644
--- a/smash/web/urls.py
+++ b/smash/web/urls.py
@@ -164,6 +164,8 @@ urlpatterns = [
         name='web.views.mail_template_edit'),
     url(r'^mail_templates/(?P<mail_template_id>\d+)/generate/(?P<instance_id>\d+)$', views.mails.generate,
         name="web.views.mail_template_generate"),
+    url(r'^mail_templates/print_vouchers$', views.mails.generate_for_vouchers,
+        name="web.views.mail_template_generate_for_vouchers"),
 
     ####################
     # DAILY PLANNING   #
diff --git a/smash/web/views/mails.py b/smash/web/views/mails.py
index 1fc450b3a55b695190667cf55c5eea8b30749df0..32a1f3b0413c30a7eb6f36825f06dda8ba805e68 100644
--- a/smash/web/views/mails.py
+++ b/smash/web/views/mails.py
@@ -11,16 +11,18 @@ from django.views.generic import DeleteView
 from django.views.generic import ListView
 from django.views.generic import UpdateView
 
+from web.docx_helper import merge_files
 from . import WrappedView
-from ..models import StudySubject, Visit, Appointment, MailTemplate
+from ..models import StudySubject, Visit, Appointment, MailTemplate, Voucher
 from ..models.constants import MAIL_TEMPLATE_CONTEXT_SUBJECT, MAIL_TEMPLATE_CONTEXT_VISIT, \
-    MAIL_TEMPLATE_CONTEXT_APPOINTMENT
+    MAIL_TEMPLATE_CONTEXT_APPOINTMENT, MAIL_TEMPLATE_CONTEXT_VOUCHER
 
 MIMETYPE_DOCX = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
 
 CONTEXT_TYPES_MAPPING = {
     MAIL_TEMPLATE_CONTEXT_SUBJECT: StudySubject,
     MAIL_TEMPLATE_CONTEXT_VISIT: Visit,
+    MAIL_TEMPLATE_CONTEXT_VOUCHER: Voucher,
     MAIL_TEMPLATE_CONTEXT_APPOINTMENT: Appointment
 }
 
@@ -35,6 +37,7 @@ class MailTemplatesListView(ListView, WrappedView):
         context['explanations'] = {"generic": MailTemplate.MAILS_TEMPLATE_GENERIC_TAGS,
                                    "subject": MailTemplate.MAILS_TEMPLATE_SUBJECT_TAGS,
                                    "visit": MailTemplate.MAILS_TEMPLATE_VISIT_TAGS,
+                                   "voucher": MailTemplate.MAILS_TEMPLATE_VOUCHER_TAGS,
                                    "appointment": MailTemplate.MAILS_TEMPLATE_APPOINTMENT_TAGS,
                                    }
         return context
@@ -47,6 +50,11 @@ class MailTemplatesCreateView(CreateView, WrappedView):
     success_url = reverse_lazy('web.views.mail_templates')
     success_message = "Template created"
 
+    def get_form(self, form_class=None):
+        form = super(MailTemplatesCreateView, self).get_form(form_class)
+        form.fields['language'].required = False
+        return form
+
 
 class MailTemplatesDeleteView(DeleteView, WrappedView):
     model = MailTemplate
@@ -78,3 +86,30 @@ def generate(request, mail_template_id, instance_id):
     response['Content-Length'] = file_size
     response['Content-Disposition'] = 'attachment; filename={}.docx'.format(mail_template.name)
     return response
+
+
+def generate_for_vouchers(request):
+    ids = request.GET.get('voucher_id', '').split(',')
+    vouchers = []
+    for voucher_id in ids:
+        if voucher_id.isdigit():
+            vouchers.append(Voucher.objects.get(pk=int(voucher_id)))
+    templates = MailTemplate.get_voucher_mail_templates([])[0]
+
+    output_stream = StringIO.StringIO()
+
+    inputs = []
+    for template in templates:
+        for voucher in vouchers:
+            input_stream = StringIO.StringIO()
+            input_stream = template.apply(voucher, request.user, input_stream)
+            input_stream.seek(0)
+            inputs.append(input_stream)
+
+    merge_files(inputs, output_stream)
+    file_size = output_stream.tell()
+    output_stream.seek(0)
+    response = HttpResponse(FileWrapper(output_stream), content_type=MIMETYPE_DOCX)
+    response['Content-Length'] = file_size
+    response['Content-Disposition'] = 'attachment; filename=vouchers.docx'
+    return response
diff --git a/smash/web/views/voucher.py b/smash/web/views/voucher.py
index 45b5a8f4f850aba0e19084b8ebefe3a2a44471dc..3124cc302b93ea9e7329ee427a2e27e5394a7184 100644
--- a/smash/web/views/voucher.py
+++ b/smash/web/views/voucher.py
@@ -10,7 +10,7 @@ from django_cron import CronJobBase, Schedule
 
 from web.views.notifications import get_today_midnight_date
 from web.forms import VoucherForm
-from web.models import Voucher, StudySubject
+from web.models import Voucher, StudySubject, MailTemplate
 from web.models.constants import GLOBAL_STUDY_ID, VOUCHER_STATUS_NEW, VOUCHER_STATUS_EXPIRED
 from . import WrappedView
 
@@ -67,6 +67,12 @@ class VoucherEditView(SuccessMessageMixin, UpdateView, WrappedView):
     def get_study_subject_id(self):
         return Voucher.objects.get(id=self.kwargs['pk']).study_subject.id
 
+    def get_context_data(self, *args, **kwargs):
+        context = super(VoucherEditView, self).get_context_data(*args, **kwargs)
+        context['mail_templates']= MailTemplate.get_voucher_mail_templates([])
+        print context
+        return context
+
 
 class ExpireVouchersJob(CronJobBase):
     RUN_EVERY_MINUTES = 120