diff --git a/.gitignore b/.gitignore
index 7230e12f66c4c676e29c1c6a86a1fbaa7aaedd4c..83c08fba63f2be651b4dbee75798abfe6c694147 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,10 @@ env/*
 # Folder with db statics (dev mode)
 smash/~/
 
+# files uploaded and hosted by django
+smash/uploads/
+smash/smash/uploads/
+
 # Disable python bytecode
 *.pyc
 #vim swap files
diff --git a/smash/web/algorithm/__init__.py b/smash/web/algorithm/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..defa4efb79e533df509ea627c72f61a3e4e001b1
--- /dev/null
+++ b/smash/web/algorithm/__init__.py
@@ -0,0 +1,4 @@
+from luhn_algorithm import LuhnAlgorithm
+from verhoeff_alogirthm import VerhoeffAlgorithm
+
+__all__ = [VerhoeffAlgorithm, LuhnAlgorithm]
diff --git a/smash/web/algorithm/luhn_algorithm.py b/smash/web/algorithm/luhn_algorithm.py
new file mode 100644
index 0000000000000000000000000000000000000000..c752bc333716c2b7e4081bae0875cdd2911ee02d
--- /dev/null
+++ b/smash/web/algorithm/luhn_algorithm.py
@@ -0,0 +1,21 @@
+class LuhnAlgorithm(object):
+    def __init__(self):
+        pass
+
+    @staticmethod
+    def luhn_checksum(card_number):
+        def digits_of(n):
+            return [int(d) for d in str(n)]
+
+        digits = digits_of(card_number)
+        odd_digits = digits[-1::-2]
+        even_digits = digits[-2::-2]
+        checksum = 0
+        checksum += sum(odd_digits)
+        for d in even_digits:
+            checksum += sum(digits_of(d * 2))
+        return checksum % 10
+
+    @staticmethod
+    def is_luhn_valid(card_number):
+        return LuhnAlgorithm.luhn_checksum(card_number) == 0
diff --git a/smash/web/algorithm/verhoeff_alogirthm.py b/smash/web/algorithm/verhoeff_alogirthm.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b9dc9ffcf0b5026bfa95845efc71b34fd8b03d4
--- /dev/null
+++ b/smash/web/algorithm/verhoeff_alogirthm.py
@@ -0,0 +1,45 @@
+verhoeff_multiplication_table = (
+    (0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
+    (1, 2, 3, 4, 0, 6, 7, 8, 9, 5),
+    (2, 3, 4, 0, 1, 7, 8, 9, 5, 6),
+    (3, 4, 0, 1, 2, 8, 9, 5, 6, 7),
+    (4, 0, 1, 2, 3, 9, 5, 6, 7, 8),
+    (5, 9, 8, 7, 6, 0, 4, 3, 2, 1),
+    (6, 5, 9, 8, 7, 1, 0, 4, 3, 2),
+    (7, 6, 5, 9, 8, 2, 1, 0, 4, 3),
+    (8, 7, 6, 5, 9, 3, 2, 1, 0, 4),
+    (9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
+verhoeff_permutation_table = (
+    (0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
+    (1, 5, 7, 6, 2, 8, 3, 0, 9, 4),
+    (5, 8, 0, 3, 7, 9, 6, 1, 4, 2),
+    (8, 9, 1, 6, 0, 4, 3, 5, 2, 7),
+    (9, 4, 5, 3, 1, 2, 6, 8, 7, 0),
+    (4, 2, 8, 6, 5, 7, 3, 9, 0, 1),
+    (2, 7, 9, 3, 8, 0, 6, 4, 1, 5),
+    (7, 0, 4, 6, 9, 1, 3, 2, 5, 8))
+
+
+class VerhoeffAlgorithm(object):
+    def __init__(self):
+        pass
+
+    @staticmethod
+    def verhoeff_checksum(number):
+        """Calculate the Verhoeff checksum over the provided number. The checksum
+        is returned as an int. Valid numbers should have a checksum of 0."""
+        # transform number list
+        number = tuple(int(n) for n in reversed(str(number)))
+        # calculate checksum
+        check = 0
+        for i, n in enumerate(number):
+            check = verhoeff_multiplication_table[check][verhoeff_permutation_table[i % 8][n]]
+        return check
+
+    @staticmethod
+    def is_valid_verhoeff(number):
+        return VerhoeffAlgorithm.verhoeff_checksum(number) == 0
+
+    @staticmethod
+    def calculate_verhoeff_check_sum(number):
+        return str(verhoeff_multiplication_table[VerhoeffAlgorithm.verhoeff_checksum(str(number) + '0')].index(0))
diff --git a/smash/web/api_views/serialization_utils.py b/smash/web/api_views/serialization_utils.py
index c1e857ed889a0973ead5f9dd0a8c5e644c32527d..89e2c9c19c2eb5aa958a17165fe12d440b842c58 100644
--- a/smash/web/api_views/serialization_utils.py
+++ b/smash/web/api_views/serialization_utils.py
@@ -41,8 +41,8 @@ def serialize_datetime(date):
 
 
 def add_column(result, name, field_name, column_list, param, columns_used_in_study=None, visible_param=None,
-               sortable=True):
-    add = True
+               sortable=True, add_param=True):
+    add = add_param
     if columns_used_in_study:
         add = getattr(columns_used_in_study, field_name)
     if add:
diff --git a/smash/web/api_views/subject.py b/smash/web/api_views/subject.py
index ac069955aae0681027e921742dbf110f7b85c4d5..589d19d633efa14f693aff4fbe624ec80c55c1b2 100644
--- a/smash/web/api_views/subject.py
+++ b/smash/web/api_views/subject.py
@@ -17,6 +17,7 @@ from web.views.notifications import get_subjects_with_no_visit, get_subjects_wit
 logger = logging.getLogger(__name__)
 
 
+# noinspection PyUnusedLocal
 @login_required
 def cities(request):
     result_subjects = Subject.objects.filter(city__isnull=False).values_list('city').distinct()
@@ -25,6 +26,7 @@ def cities(request):
     })
 
 
+# noinspection PyUnusedLocal
 @login_required
 def referrals(request):
     result_subjects = StudySubject.objects.filter(referral__isnull=False).values_list('referral').distinct()
@@ -51,10 +53,17 @@ def get_subject_columns(request, subject_list_type):
     add_column(result, "Screening", "screening_number", study_subject_columns, "string_filter", study.columns)
     add_column(result, "First name", "first_name", subject_columns, "string_filter")
     add_column(result, "Last name", "last_name", subject_columns, "string_filter")
+    add_column(result, "Social Security Number", "social_security_number", subject_columns, "string_filter")
     add_column(result, "Date of birth", "date_born", subject_columns, None)
     add_column(result, "Contact on", "datetime_contact_reminder", study_subject_columns, None, study.columns)
     add_column(result, "Last contact attempt", "last_contact_attempt", study_subject_list, None)
     add_column(result, "Referred by", "referral", study_subject_columns, "string_filter", study.columns)
+    add_column(result, "Health partner name", "health_partner_first_name", None, "string_filter",
+               add_param=study.columns.health_partner,
+               visible_param=study_subject_columns.health_partner)
+    add_column(result, "Health partner last name", "health_partner_last_name", None, "string_filter",
+               add_param=study.columns.health_partner,
+               visible_param=study_subject_columns.health_partner)
     add_column(result, "Location", "default_location", study_subject_columns, "location_filter", study.columns)
     add_column(result, "Flying team location", "flying_team", study_subject_columns, "flying_team_filter",
                study.columns)
@@ -116,6 +125,12 @@ def get_subjects_order(subjects_to_be_ordered, order_column, order_direction):
         result = subjects_to_be_ordered.order_by(order_direction + 'resigned')
     elif order_column == "information_sent":
         result = subjects_to_be_ordered.order_by(order_direction + 'information_sent')
+    elif order_column == "health_partner_first_name":
+        result = subjects_to_be_ordered.order_by(order_direction + 'health_partner__first_name')
+    elif order_column == "health_partner_last_name":
+        result = subjects_to_be_ordered.order_by(order_direction + 'health_partner__last_name')
+    elif order_column == "social_security_number":
+        result = subjects_to_be_ordered.order_by(order_direction + 'subject__social_security_number')
     elif order_column == "postponed":
         result = subjects_to_be_ordered.order_by(order_direction + 'postponed')
     elif order_column == "type":
@@ -226,6 +241,12 @@ def get_subjects_filtered(subjects_to_be_filtered, filters):
             result = result.filter(postponed=(value == "true"))
         elif column == "information_sent":
             result = result.filter(information_sent=(value == "true"))
+        elif column == "health_partner_first_name":
+            result = result.filter(health_partner__first_name__icontains=value)
+        elif column == "health_partner_last_name":
+            result = result.filter(health_partner__last_name__icontains=value)
+        elif column == "social_security_number":
+            result = result.filter(subject__social_security_number__icontains=value)
         elif column == "default_location":
             result = result.filter(default_location=value)
         elif column == "flying_team":
@@ -244,7 +265,6 @@ def get_subjects_filtered(subjects_to_be_filtered, filters):
             else:
                 message += str(column)
             logger.warn(message)
-
     return result
 
 
@@ -289,6 +309,7 @@ def subjects(request, type):
         return e500_error(request)
 
 
+# noinspection PyUnusedLocal
 @login_required
 def types(request):
     data = [{"id": subject_type_id, "name": subject_type_name} for subject_type_id, subject_type_name in
@@ -338,6 +359,12 @@ def serialize_subject(study_subject):
 
     else:
         last_contact_attempt_string = ""
+    health_partner_first_name = ""
+    if study_subject.health_partner:
+        health_partner_first_name = study_subject.health_partner.first_name
+    health_partner_last_name = ""
+    if study_subject.health_partner:
+        health_partner_last_name = study_subject.health_partner.last_name
     result = {
         "first_name": study_subject.subject.first_name,
         "last_name": study_subject.subject.last_name,
@@ -353,6 +380,9 @@ def serialize_subject(study_subject):
         "resigned": bool_to_yes_no(study_subject.resigned),
         "postponed": bool_to_yes_no(study_subject.postponed),
         "information_sent": bool_to_yes_no(study_subject.information_sent),
+        "health_partner_first_name": health_partner_first_name,
+        "health_partner_last_name": health_partner_last_name,
+        "social_security_number": study_subject.subject.social_security_number,
         "type": study_subject.get_type_display(),
         "id": study_subject.id,
         "visits": serialized_visits,
diff --git a/smash/web/forms/__init__.py b/smash/web/forms/__init__.py
index 41432b74e56c6ac250f4f182d9654a39f6f7f772..23d44c9490bb74edddc5ec5ad5d436dffd829ae2 100644
--- a/smash/web/forms/__init__.py
+++ b/smash/web/forms/__init__.py
@@ -4,8 +4,10 @@ from forms import WorkerAddForm, \
     AvailabilityEditForm, HolidayAddForm
 from study_subject_forms import StudySubjectAddForm, StudySubjectDetailForm, StudySubjectEditForm
 from subject_forms import SubjectAddForm, SubjectEditForm, SubjectDetailForm
+from voucher_forms import VoucherTypeForm, VoucherTypePriceForm, VoucherForm
 
 __all__ = [StudySubjectAddForm, StudySubjectDetailForm, StudySubjectEditForm, WorkerAddForm, WorkerEditForm,
            AppointmentDetailForm, AppointmentEditForm, AppointmentAddForm, VisitDetailForm, VisitAddForm,
            ContactAttemptForm, ContactAttemptEditForm, KitRequestForm, StatisticsForm, AvailabilityAddForm,
-           AvailabilityEditForm, HolidayAddForm, SubjectAddForm, SubjectEditForm, SubjectDetailForm]
+           AvailabilityEditForm, HolidayAddForm, SubjectAddForm, SubjectEditForm, SubjectDetailForm, VoucherTypeForm,
+           VoucherTypePriceForm, VoucherForm]
diff --git a/smash/web/forms/forms.py b/smash/web/forms/forms.py
index 92088a283f2579b97eaa399a20937231ed855d74..1aa33b920dd8735547cc2b8c786b81ef79e369d6 100644
--- a/smash/web/forms/forms.py
+++ b/smash/web/forms/forms.py
@@ -400,20 +400,3 @@ class HolidayAddForm(ModelForm):
             validate_availability_conflict(self, self.cleaned_data, availability)
 
 
-class VoucherTypeForm(ModelForm):
-    class Meta:
-        model = VoucherType
-        exclude = ['study']
-
-
-class VoucherTypePriceForm(ModelForm):
-    start_date = forms.DateField(label="Start date",
-                                 widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d")
-                                 )
-    end_date = forms.DateField(label="End date",
-                               widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d")
-                               )
-
-    class Meta:
-        model = VoucherTypePrice
-        exclude = ['voucher_type']
diff --git a/smash/web/forms/study_subject_forms.py b/smash/web/forms/study_subject_forms.py
index aacd999186c4d76bd08a7545369d488530456558..94fa1fcfe15551d3d9510337db6ad1828158ca24 100644
--- a/smash/web/forms/study_subject_forms.py
+++ b/smash/web/forms/study_subject_forms.py
@@ -5,18 +5,31 @@ from django import forms
 from django.forms import ModelForm
 
 from web.forms.forms import DATETIMEPICKER_DATE_ATTRS, get_worker_from_args
-from web.models import StudySubject, Study, StudyColumns
+from web.models import StudySubject, Study, StudyColumns, VoucherType
 from web.models.constants import SCREENING_NUMBER_PREFIXES_FOR_TYPE
+from web.widgets.secure_file_widget import SecuredFileWidget
 
 logger = logging.getLogger(__name__)
 
 
-class StudySubjectAddForm(ModelForm):
+class StudySubjectForm(ModelForm):
     datetime_contact_reminder = forms.DateTimeField(label="Contact on",
                                                     widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS),
                                                     required=False
                                                     )
 
+    referral_letter = forms.FileField(label='Select a file', widget=SecuredFileWidget(), required=False)
+
+    voucher_types = forms.ModelMultipleChoiceField(required=False,
+                                                   widget=forms.CheckboxSelectMultiple,
+                                                   queryset=VoucherType.objects.all(),
+                                                   )
+
+    def __init__(self, *args, **kwargs):
+        super(StudySubjectForm, self).__init__(*args, **kwargs)
+
+
+class StudySubjectAddForm(StudySubjectForm):
     class Meta:
         model = StudySubject
         fields = '__all__'
@@ -26,12 +39,12 @@ class StudySubjectAddForm(ModelForm):
         self.user = get_worker_from_args(kwargs)
         self.study = get_study_from_args(kwargs)
 
-        super(ModelForm, self).__init__(*args, **kwargs)
+        super(StudySubjectAddForm, self).__init__(*args, **kwargs)
         prepare_study_subject_fields(fields=self.fields, study=self.study)
 
     def save(self, commit=True):
         self.instance.study_id = self.study.id
-        return super(ModelForm, self).save(commit)
+        return super(StudySubjectAddForm, self).save(commit)
 
     def build_screening_number(self, cleaned_data):
         screening_number = cleaned_data.get('screening_number', None)
@@ -83,7 +96,7 @@ def get_new_screening_number(screening_number_prefix):
     return screening_number_prefix + str(result_number + 1).zfill(3)
 
 
-class StudySubjectDetailForm(ModelForm):
+class StudySubjectDetailForm(StudySubjectForm):
     class Meta:
         model = StudySubject
         fields = '__all__'
@@ -104,11 +117,7 @@ def get_study_from_study_subject_instance(study_subject):
         return Study(columns=StudyColumns())
 
 
-class StudySubjectEditForm(ModelForm):
-    datetime_contact_reminder = forms.DateTimeField(label="Contact on",
-                                                    widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS),
-                                                    required=False
-                                                    )
+class StudySubjectEditForm(StudySubjectForm):
 
     def __init__(self, *args, **kwargs):
         was_resigned = kwargs.get('was_resigned', False)
@@ -166,6 +175,12 @@ def prepare_study_subject_fields(fields, study):
     prepare_field(fields, study.columns, 'pd_in_family')
     prepare_field(fields, study.columns, 'resigned')
     prepare_field(fields, study.columns, 'resign_reason')
+    prepare_field(fields, study.columns, 'referral_letter')
+    prepare_field(fields, study.columns, 'health_partner')
+    prepare_field(fields, study.columns, 'health_partner_feedback_agreement')
+    prepare_field(fields, study.columns, 'screening')
+    prepare_field(fields, study.columns, 'previously_in_study')
+    prepare_field(fields, study.columns, 'voucher_types')
 
 
 def validate_subject_screening_number(self, cleaned_data):
diff --git a/smash/web/forms/subject_forms.py b/smash/web/forms/subject_forms.py
index b4f12f65c5eb51b4bf0f221517d04123fc49387e..05fe533ea79251c871e1a6c9b66454384758e437 100644
--- a/smash/web/forms/subject_forms.py
+++ b/smash/web/forms/subject_forms.py
@@ -1,16 +1,40 @@
+import logging
+
 from django import forms
 from django.forms import ModelForm
 
+from web.algorithm import VerhoeffAlgorithm, LuhnAlgorithm
 from web.forms.forms import DATEPICKER_DATE_ATTRS
 from web.models import Subject
 from web.models.constants import COUNTRY_OTHER_ID
 
+logger = logging.getLogger(__name__)
+
 
 def validate_subject_country(self, cleaned_data):
     if cleaned_data['country'].id == COUNTRY_OTHER_ID:
         self.add_error('country', "Select valid country")
 
 
+def validate_social_security_number(self, number):
+    if not is_valid_social_security_number(number):
+        self.add_error('social_security_number', "Social security number is invalid")
+
+
+def is_valid_social_security_number(number):
+    if number is not None and number != '':
+        if len(number) != 13:
+            return False
+        if not number.isdigit():
+            return False
+        if not LuhnAlgorithm.is_luhn_valid(number[:12]):
+            return False
+        if not VerhoeffAlgorithm.is_valid_verhoeff(number[:11] + number[12]):
+            return False
+
+    return True
+
+
 FIELD_ORDER = ["first_name", "last_name", "sex", "date_born", "social_security_number",
                "default_written_communication_language", "languages", "phone_number", "phone_number_2",
                "phone_number_3", "address", "city", "postal_code", "country"]
@@ -32,6 +56,7 @@ class SubjectAddForm(ModelForm):
     def clean(self):
         cleaned_data = super(SubjectAddForm, self).clean()
         validate_subject_country(self, cleaned_data)
+        validate_social_security_number(self, cleaned_data["social_security_number"])
         return cleaned_data
 
 
@@ -53,6 +78,7 @@ class SubjectEditForm(ModelForm):
 
     def clean(self):
         validate_subject_country(self, self.cleaned_data)
+        validate_social_security_number(self, self.cleaned_data["social_security_number"])
 
     class Meta:
         model = Subject
diff --git a/smash/web/forms/voucher_forms.py b/smash/web/forms/voucher_forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..99bb89736762a3c49a1e859d95feb0389f033fa9
--- /dev/null
+++ b/smash/web/forms/voucher_forms.py
@@ -0,0 +1,81 @@
+import datetime
+import logging
+
+from django import forms
+from django.forms import ModelForm
+from django.utils import timezone
+
+from web.algorithm import VerhoeffAlgorithm
+from web.forms.forms import DATEPICKER_DATE_ATTRS
+from web.models import VoucherType, VoucherTypePrice, Voucher
+from web.models.constants import VOUCHER_STATUS_NEW, VOUCHER_STATUS_USED
+
+logger = logging.getLogger(__name__)
+
+
+class VoucherTypeForm(ModelForm):
+    class Meta:
+        model = VoucherType
+        exclude = ['study']
+
+
+class VoucherTypePriceForm(ModelForm):
+    start_date = forms.DateField(label="Start date",
+                                 widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d")
+                                 )
+    end_date = forms.DateField(label="End date",
+                               widget=forms.DateInput(DATEPICKER_DATE_ATTRS, "%Y-%m-%d")
+                               )
+
+    class Meta:
+        model = VoucherTypePrice
+        exclude = ['voucher_type']
+
+
+class VoucherForm(ModelForm):
+    class Meta:
+        model = Voucher
+        fields = '__all__'
+
+    def __init__(self, *args, **kwargs):
+        voucher_types = kwargs.pop('voucher_types', VoucherType.objects.all())
+        super(VoucherForm, self).__init__(*args, **kwargs)
+
+        self.fields['voucher_type'].queryset = voucher_types
+
+        self.fields['number'].widget.attrs['readonly'] = True
+        self.fields['number'].required = False
+
+        self.fields['issue_date'].widget.attrs['readonly'] = True
+        self.fields['issue_date'].required = False
+        self.fields['expiry_date'].widget.attrs['readonly'] = True
+        self.fields['expiry_date'].required = False
+        self.fields['use_date'].widget.attrs['readonly'] = True
+        instance = getattr(self, 'instance', None)
+        if instance and instance.pk:
+            self.fields['voucher_type'].widget.attrs['readonly'] = True
+            if instance.status != VOUCHER_STATUS_NEW:
+                self.fields['status'].widget.attrs['readonly'] = True
+                self.fields['feedback'].widget.attrs['readonly'] = True
+                self.fields['usage_partner'].widget.attrs['readonly'] = True
+
+    def clean(self):
+        if self.cleaned_data["status"] == VOUCHER_STATUS_USED and not self.cleaned_data["usage_partner"]:
+            self.add_error('usage_partner', "Partner must be defined for used voucher")
+        if self.cleaned_data["status"] != VOUCHER_STATUS_USED and self.cleaned_data["usage_partner"]:
+            self.add_error('status', "Status must be used for voucher with defined partner")
+
+    def save(self, commit=True):
+        instance = super(VoucherForm, self).save(commit=False)
+        if not instance.id:
+            instance.issue_date = timezone.now()
+            instance.expiry_date = instance.issue_date + datetime.timedelta(days=92)
+            max_id = str(0).zfill(5)
+            if Voucher.objects.all().count() > 0:
+                max_id = str(Voucher.objects.latest('id').id).zfill(5)
+            instance.number = max_id + VerhoeffAlgorithm.calculate_verhoeff_check_sum(max_id)
+        if instance.status == VOUCHER_STATUS_USED and not instance.use_date:
+            instance.use_date = timezone.now()
+
+        if commit:
+            instance.save()
diff --git a/smash/web/migrations/0091_auto_20171208_1312.py b/smash/web/migrations/0091_auto_20171208_1312.py
new file mode 100644
index 0000000000000000000000000000000000000000..3c823e288d5e639c074ca55b08041f45aa18d1c7
--- /dev/null
+++ b/smash/web/migrations/0091_auto_20171208_1312.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-08 13:12
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('web', '0090_vouchertype_vouchertypeprice'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='vouchertypeprice',
+            name='voucher_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prices', to='web.VoucherType'),
+        ),
+    ]
diff --git a/smash/web/migrations/0092_voucher.py b/smash/web/migrations/0092_voucher.py
new file mode 100644
index 0000000000000000000000000000000000000000..963120ee80b966459eebaf0c9490d56b9ca37a85
--- /dev/null
+++ b/smash/web/migrations/0092_voucher.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-08 15:00
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('web', '0091_auto_20171208_1312'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Voucher',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('number', models.CharField(max_length=10, unique=True, verbose_name=b'Number')),
+                ('issue_date', models.DateField(verbose_name=b'Issue date')),
+                ('expiry_date', models.DateField(verbose_name=b'Expiry date')),
+                ('use_date', models.DateField(verbose_name=b'Use date')),
+                ('status', models.CharField(choices=[(b'NEW', b'New'), (b'USED', b'Used'), (b'EXPIRED', b'Expired')], default=b'NEW', max_length=20, verbose_name=b'Status')),
+                ('feedback', models.TextField(blank=True, max_length=2000, verbose_name=b'Feedback')),
+                ('study_subject', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to='web.StudySubject')),
+                ('usage_partner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.Worker')),
+                ('voucher_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.VoucherType')),
+            ],
+        ),
+    ]
diff --git a/smash/web/migrations/0093_auto_20171208_1508.py b/smash/web/migrations/0093_auto_20171208_1508.py
new file mode 100644
index 0000000000000000000000000000000000000000..839c2936a2f41311e6f79358abe979c4abe41b83
--- /dev/null
+++ b/smash/web/migrations/0093_auto_20171208_1508.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-08 15:08
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('web', '0092_voucher'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='voucher',
+            name='usage_partner',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='web.Worker'),
+        ),
+    ]
diff --git a/smash/web/migrations/0094_auto_20171208_1508.py b/smash/web/migrations/0094_auto_20171208_1508.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ca263649479e21e768968f07d8db7ba7d3d65ee
--- /dev/null
+++ b/smash/web/migrations/0094_auto_20171208_1508.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-08 15:08
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('web', '0093_auto_20171208_1508'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='voucher',
+            name='usage_partner',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='web.Worker'),
+        ),
+    ]
diff --git a/smash/web/migrations/0095_auto_20171208_1509.py b/smash/web/migrations/0095_auto_20171208_1509.py
new file mode 100644
index 0000000000000000000000000000000000000000..37b50905594f0b4d2e34c07991813bb36b5fe7a9
--- /dev/null
+++ b/smash/web/migrations/0095_auto_20171208_1509.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-08 15:09
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('web', '0094_auto_20171208_1508'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='voucher',
+            name='use_date',
+            field=models.DateField(null=True, verbose_name=b'Use date'),
+        ),
+    ]
diff --git a/smash/web/migrations/0096_auto_20171208_1509.py b/smash/web/migrations/0096_auto_20171208_1509.py
new file mode 100644
index 0000000000000000000000000000000000000000..b076d5c3f0bcb912bab2db36a94f67bc15746c85
--- /dev/null
+++ b/smash/web/migrations/0096_auto_20171208_1509.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-08 15:09
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('web', '0095_auto_20171208_1509'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='voucher',
+            name='use_date',
+            field=models.DateField(blank=True, null=True, verbose_name=b'Use date'),
+        ),
+    ]
diff --git a/smash/web/migrations/0097_auto_20171211_1616.py b/smash/web/migrations/0097_auto_20171211_1616.py
new file mode 100644
index 0000000000000000000000000000000000000000..676c94f20f7f1553d4cc15445331f6b18548b849
--- /dev/null
+++ b/smash/web/migrations/0097_auto_20171211_1616.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-11 16:16
+from __future__ import unicode_literals
+
+import django.core.files.storage
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('web', '0096_auto_20171208_1509'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='studycolumns',
+            name='health_partner',
+            field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=False, verbose_name=b'Health partner'),
+        ),
+        migrations.AddField(
+            model_name='studycolumns',
+            name='health_partner_feedback_agreement',
+            field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=False, verbose_name=b'Agrees to give information to referral'),
+        ),
+        migrations.AddField(
+            model_name='studycolumns',
+            name='previously_in_study',
+            field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=False, verbose_name=b'Previously in PDP study'),
+        ),
+        migrations.AddField(
+            model_name='studycolumns',
+            name='referral_letter',
+            field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=False, verbose_name=b'Referral letter'),
+        ),
+        migrations.AddField(
+            model_name='studycolumns',
+            name='screening',
+            field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=False, verbose_name=b'Screening'),
+        ),
+        migrations.AddField(
+            model_name='studycolumns',
+            name='voucher_types',
+            field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=False, verbose_name=b'Voucher types'),
+        ),
+        migrations.AddField(
+            model_name='studycolumns',
+            name='vouchers',
+            field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=False, verbose_name=b'Vouchers'),
+        ),
+        migrations.AddField(
+            model_name='studysubject',
+            name='health_partner',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='web.Worker', verbose_name=b'Health partner'),
+        ),
+        migrations.AddField(
+            model_name='studysubject',
+            name='health_partner_feedback_agreement',
+            field=models.BooleanField(default=False, verbose_name=b'Agrees to give information to referral'),
+        ),
+        migrations.AddField(
+            model_name='studysubject',
+            name='previously_in_study',
+            field=models.BooleanField(default=False, verbose_name=b'Previously in PDP study'),
+        ),
+        migrations.AddField(
+            model_name='studysubject',
+            name='referral_letter',
+            field=models.FileField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(location=b'uploads'), upload_to=b'referral_letters', verbose_name=b'Referral letter'),
+        ),
+        migrations.AddField(
+            model_name='studysubject',
+            name='screening',
+            field=models.CharField(blank=True, max_length=1024, null=True, verbose_name=b'Screening'),
+        ),
+        migrations.AddField(
+            model_name='studysubject',
+            name='voucher_types',
+            field=models.ManyToManyField(blank=True, to='web.VoucherType', verbose_name=b'Voucher types'),
+        ),
+        migrations.AlterField(
+            model_name='studycolumns',
+            name='datetime_contact_reminder',
+            field=models.BooleanField(choices=[(True, b'Yes'), (False, b'No')], default=True, verbose_name=b'Please make a contact on'),
+        ),
+        migrations.AlterField(
+            model_name='studysubject',
+            name='diagnosis',
+            field=models.CharField(blank=True, max_length=1024, null=True, verbose_name=b'Diagnosis'),
+        ),
+        migrations.AlterField(
+            model_name='voucher',
+            name='study_subject',
+            field=models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='vouchers', to='web.StudySubject'),
+        ),
+    ]
diff --git a/smash/web/models/__init__.py b/smash/web/models/__init__.py
index 6b9eac7eed601cc8b85949cb752d87ecce2f2764..77a30db094dfd4b977b2ca164a0070b39ea1e8cc 100644
--- a/smash/web/models/__init__.py
+++ b/smash/web/models/__init__.py
@@ -27,6 +27,7 @@ from item import Item
 from language import Language
 from subject import Subject
 from study_subject import StudySubject
+from voucher import Voucher
 from study_subject_list import StudySubjectList
 from study_visit_list import StudyVisitList
 from appointment_list import AppointmentList
@@ -38,5 +39,5 @@ from inconsistent_subject import InconsistentSubject, InconsistentField
 __all__ = [Study, FlyingTeam, Appointment, AppointmentType, Availability, Holiday, Item, Language, Location, Room,
            Subject, StudySubject, StudySubjectList, SubjectColumns, StudyNotificationParameters,
            AppointmentList, AppointmentColumns, Visit, Worker, ContactAttempt, ConfigurationItem, MailTemplate,
-           AppointmentTypeLink, VoucherType, VoucherTypePrice,
+           AppointmentTypeLink, VoucherType, VoucherTypePrice, Voucher,
            MissingSubject, InconsistentSubject, InconsistentField, Country, StudyColumns, VisitColumns, StudyVisitList]
diff --git a/smash/web/models/constants.py b/smash/web/models/constants.py
index fe6ade3b9b0c61fb9df46206f7c427d0735157f9..5db58bb977dc42c08b437e45c91ca1e542e8be0b 100644
--- a/smash/web/models/constants.py
+++ b/smash/web/models/constants.py
@@ -1,6 +1,8 @@
 # coding=utf-8
 import locale
 
+from django.core.files.storage import FileSystemStorage
+
 BOOL_CHOICES = ((True, 'Yes'), (False, 'No'))
 SEX_CHOICES_MALE = 'M'
 SEX_CHOICES_FEMALE = 'F'
@@ -81,3 +83,14 @@ COUNTRY_AFGHANISTAN_ID = 2
 # id of the singleton Study,
 # TODO remove after allowing many studies per Smasch instance
 GLOBAL_STUDY_ID = 1
+
+VOUCHER_STATUS_NEW = "NEW"
+VOUCHER_STATUS_USED = "USED"
+VOUCHER_STATUS_EXPIRED = "EXPIRED"
+VOUCHER_STATUS_CHOICES = (
+    (VOUCHER_STATUS_NEW, 'New'),
+    (VOUCHER_STATUS_USED, 'Used'),
+    (VOUCHER_STATUS_EXPIRED, 'Expired'),
+)
+
+FILE_STORAGE = FileSystemStorage(location='uploads')
diff --git a/smash/web/models/study_columns.py b/smash/web/models/study_columns.py
index 7700a61be37532167f324d701fd8c771d9aecbe1..7acd12ba62fbfe7c027c02d5943bd28723b80c2d 100644
--- a/smash/web/models/study_columns.py
+++ b/smash/web/models/study_columns.py
@@ -78,7 +78,37 @@ class StudyColumns(models.Model):
                                         verbose_name='Resign reason'
                                         )
 
-    datetime_contact_reminder = models.BooleanField(choices=BOOL_CHOICES,
-                                                    default=True,
-                                                    verbose_name='Last contact attempt'
-                                                    )
+    referral_letter = models.BooleanField(choices=BOOL_CHOICES,
+                                          default=False,
+                                          verbose_name='Referral letter'
+                                          )
+
+    health_partner = models.BooleanField(choices=BOOL_CHOICES,
+                                         default=False,
+                                         verbose_name='Health partner'
+                                         )
+
+    health_partner_feedback_agreement = models.BooleanField(choices=BOOL_CHOICES,
+                                                            default=False,
+                                                            verbose_name='Agrees to give information to referral'
+                                                            )
+
+    screening = models.BooleanField(choices=BOOL_CHOICES,
+                                    default=False,
+                                    verbose_name='Screening'
+                                    )
+
+    previously_in_study = models.BooleanField(choices=BOOL_CHOICES,
+                                              default=False,
+                                              verbose_name='Previously in PDP study',
+                                              )
+
+    voucher_types = models.BooleanField(choices=BOOL_CHOICES,
+                                        default=False,
+                                        verbose_name='Voucher types',
+                                        )
+
+    vouchers = models.BooleanField(choices=BOOL_CHOICES,
+                                   default=False,
+                                   verbose_name='Vouchers',
+                                   )
diff --git a/smash/web/models/study_subject.py b/smash/web/models/study_subject.py
index 8bbeae76e223005167fc857d97465eff1c92af4f..705bd1905411777e0de4a7b57a52960f13278e99 100644
--- a/smash/web/models/study_subject.py
+++ b/smash/web/models/study_subject.py
@@ -1,9 +1,8 @@
 # coding=utf-8
-from django.core.validators import RegexValidator
 from django.db import models
 
-from constants import BOOL_CHOICES, SUBJECT_TYPE_CHOICES
-from . import Appointment, Location, Visit
+from web.models import VoucherType, Appointment, Location, Visit
+from web.models.constants import BOOL_CHOICES, SUBJECT_TYPE_CHOICES, FILE_STORAGE
 
 
 class StudySubject(models.Model):
@@ -92,11 +91,47 @@ class StudySubject(models.Model):
                                 blank=True,
                                 verbose_name='Referred by'
                                 )
-    diagnosis = models.CharField(max_length=128,
+    referral_letter = models.FileField(
+        storage=FILE_STORAGE,
+        upload_to='referral_letters',
+        verbose_name='Referral letter',
+        blank=True,
+        null=True,
+    )
+
+    health_partner = models.ForeignKey("web.Worker",
+                                       verbose_name='Health partner',
+                                       null=True,
+                                       blank=True
+                                       )
+
+    health_partner_feedback_agreement = models.BooleanField(
+        verbose_name='Agrees to give information to referral',
+        default=False,
+    )
+
+    screening = models.CharField(max_length=1024,
+                                 null=True,
+                                 blank=True,
+                                 verbose_name='Screening'
+                                 )
+
+    diagnosis = models.CharField(max_length=1024,
                                  null=True,
                                  blank=True,
                                  verbose_name='Diagnosis'
                                  )
+
+    previously_in_study = models.BooleanField(
+        verbose_name='Previously in PDP study',
+        default=False,
+    )
+
+    voucher_types = models.ManyToManyField(VoucherType,
+                                           blank=True,
+                                           verbose_name='Voucher types'
+                                           )
+
     year_of_diagnosis = models.IntegerField(
         null=True,
         blank=True,
diff --git a/smash/web/models/voucher.py b/smash/web/models/voucher.py
new file mode 100644
index 0000000000000000000000000000000000000000..340b78764e16e537b72809a29f767811b105650d
--- /dev/null
+++ b/smash/web/models/voucher.py
@@ -0,0 +1,59 @@
+# coding=utf-8
+
+from django.db import models
+
+from web.models import VoucherType, StudySubject, Worker
+from web.models.constants import VOUCHER_STATUS_CHOICES, VOUCHER_STATUS_NEW
+
+
+class Voucher(models.Model):
+    class Meta:
+        app_label = 'web'
+
+    number = models.CharField(
+        max_length=10,
+        verbose_name='Number',
+        blank=False,
+        null=False,
+        unique=True
+    )
+
+    issue_date = models.DateField(verbose_name='Issue date', null=False)
+    expiry_date = models.DateField(verbose_name='Expiry date', null=False)
+    use_date = models.DateField(verbose_name='Use date', null=True, blank=True)
+    voucher_type = models.ForeignKey(
+        VoucherType,
+        on_delete=models.CASCADE,
+        null=False,
+    )
+
+    study_subject = models.ForeignKey(
+        StudySubject,
+        on_delete=models.CASCADE,
+        null=False,
+        related_name="vouchers",
+        editable=False
+    )
+
+    status = models.CharField(max_length=20, choices=VOUCHER_STATUS_CHOICES,
+                              verbose_name='Status',
+                              default=VOUCHER_STATUS_NEW
+                              )
+
+    feedback = models.TextField(max_length=2000,
+                                blank=True,
+                                verbose_name='Feedback'
+                                )
+
+    usage_partner = models.ForeignKey(
+        Worker,
+        on_delete=models.CASCADE,
+        null=True,
+        blank=True
+    )
+
+    def __str__(self):
+        return "%s - %s %s" % (self.number, self.study_subject.subject.first_name, self.study_subject.subject.last_name)
+
+    def __unicode__(self):
+        return "%s - %s %s" % (self.number, self.study_subject.subject.first_name, self.study_subject.subject.last_name)
diff --git a/smash/web/templates/appointments/edit.html b/smash/web/templates/appointments/edit.html
index 1d7cfa2ab4d86d3105f38041af240c4202339785..dc1df827cfd7967dbad32f7e7e69ae79545bacd9 100644
--- a/smash/web/templates/appointments/edit.html
+++ b/smash/web/templates/appointments/edit.html
@@ -35,7 +35,7 @@
         </div>
         <div class="box box-info">
 
-            <form method="post" action="" class="form-horizontal">
+            <form method="post" action="" enctype="multipart/form-data" class="form-horizontal">
                 {% csrf_token %}
                 <fieldset>
                     <div class="box-header with-border">
@@ -136,6 +136,11 @@
                 </div><!-- /.box-footer -->
             </form>
         </div>
+
+        {% if appointment.visit.subject.study.columns.vouchers %}
+            {% include 'includes/subject_vouchers_box.html' with subject=appointment.visit.subject %}
+        {% endif %}
+
         {% include 'includes/mail_templates_box.html' with instance_id=appointment.id %}
         {% if appointment.visit %}
             {% include 'includes/contact_attempts_box.html' with subject=appointment.visit.subject contact_attempts=contact_attempts appointment_id=appointment.id %}
diff --git a/smash/web/templates/includes/subject_vouchers_box.html b/smash/web/templates/includes/subject_vouchers_box.html
new file mode 100644
index 0000000000000000000000000000000000000000..36638b10787183d64546baaa7fc5d2ec3810f496
--- /dev/null
+++ b/smash/web/templates/includes/subject_vouchers_box.html
@@ -0,0 +1,48 @@
+<div class="row">
+    <div class="col-lg-12">
+        <div class="box box-success">
+            <div class="box-header with-border">
+                <h3>Vouchers <a title="add a new voucher"
+                                id="add-voucher"
+                                href="{% url 'web.views.voucher_add' %}?study_subject_id={{ subject.id }}"
+                                class="text-primary"
+                ><i class="fa fa-plus-circle text-success"></i></a></h3>
+            </div>
+            <div class="box-body">
+                <table class="table table-bordered table-striped">
+                    <thead>
+                    <tr>
+
+                        <th class="text-center">Number</th>
+                        <th class="text-center">Type</th>
+                        <th class="text-center">Issue date</th>
+                        <th class="text-center">Expiry date</th>
+                        <th class="text-center">Status</th>
+                        <th class="text-center">Use date</th>
+                        <th class="text-center">Partner</th>
+                        <th class="text-center">Feedback</th>
+                        <th class="text-center">Edit</th>
+                    </tr>
+                    </thead>
+                    <tbody>
+                    {% for voucher in subject.vouchers.all %}
+                        <tr>
+                            <td>{{ voucher.number }}</td>
+                            <td>{{ voucher.voucher_type }}</td>
+                            <td>{{ voucher.issue_date }}</td>
+                            <td>{{ voucher.expiry_date }}</td>
+                            <td>{{ voucher.status }}</td>
+                            <td>{{ voucher.use_date }}</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>
+            </div>
+        </div>
+
+    </div>
+</div>
diff --git a/smash/web/templates/sidebar.html b/smash/web/templates/sidebar.html
index 70f1ff705b17df0f826c47b921d33f2ed6741c24..05498fc5bf407bdd307a8e554214a598a5105953 100644
--- a/smash/web/templates/sidebar.html
+++ b/smash/web/templates/sidebar.html
@@ -63,6 +63,14 @@
             <span>Export</span>
         </a>
     </li>
+
+    <li data-desc="vouchers">
+        <a href="{% url 'web.views.vouchers' %}">
+            <i class="fa fa-user-md"></i>
+            <span>Vouchers</span>
+        </a>
+    </li>
+
     <li data-desc="configuration" class="treeview">
         <a href="#">
             <i class="fa fa-wrench"></i> <span>Configuration</span>
diff --git a/smash/web/templates/subjects/add.html b/smash/web/templates/subjects/add.html
index 3bacf8021599c202596190da36dc00db059e1f88..bf01448f4625f7028478f21787460fabb27497d8 100644
--- a/smash/web/templates/subjects/add.html
+++ b/smash/web/templates/subjects/add.html
@@ -31,7 +31,7 @@
         </div>
         <div class="row">
 
-            <form method="post" action="" class="form-horizontal">
+            <form method="post" action="" enctype="multipart/form-data"  class="form-horizontal">
                 {% csrf_token %}
                 <div class="col-md-12">
                     <div class="box box-body">
diff --git a/smash/web/templates/subjects/edit.html b/smash/web/templates/subjects/edit.html
index 7e956ad711830c9c1e318edb5b474346910ff521..669f863086174f60aaca05907bf6713c84725340 100644
--- a/smash/web/templates/subjects/edit.html
+++ b/smash/web/templates/subjects/edit.html
@@ -41,7 +41,7 @@
                         <h3>Subject details</h3>
                     </div>
 
-                    <form method="post" action="" class="form-horizontal">
+                    <form method="post" action="" enctype="multipart/form-data" class="form-horizontal">
                         {% csrf_token %}
                         <div class="box-body">
                             <div class="col-md-12">
@@ -109,11 +109,16 @@
             </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 %}
 
 
+
         <div class="modal modal-danger fade" id="confirm-dead-resigned-mark-dialog" tabindex="-1" role="dialog">
             <div class="modal-dialog" role="document">
                 <div class="modal-content">
diff --git a/smash/web/templates/vouchers/add.html b/smash/web/templates/vouchers/add.html
new file mode 100644
index 0000000000000000000000000000000000000000..994ab3cb9337086474d330f2b288d6a4aaa2bba5
--- /dev/null
+++ b/smash/web/templates/vouchers/add.html
@@ -0,0 +1,9 @@
+{% extends "vouchers/add_edit.html" %}
+
+{% block page_header %}New voucher{% endblock page_header %}
+
+{% block title %}{{ block.super }} - Add voucher{% endblock %}
+
+{% block form-title %}Enter voucher details{% endblock %}
+
+{% block save-button %}Add{% endblock %}
diff --git a/smash/web/templates/vouchers/add_edit.html b/smash/web/templates/vouchers/add_edit.html
new file mode 100644
index 0000000000000000000000000000000000000000..8e5e5dfbaf497bf4a88f70ded534fd2e8ef92a8f
--- /dev/null
+++ b/smash/web/templates/vouchers/add_edit.html
@@ -0,0 +1,77 @@
+{% extends "_base.html" %}
+{% load static %}
+{% load filters %}
+
+{% block styles %}
+    {{ block.super }}
+    <link rel="stylesheet" href="{% static 'AdminLTE/plugins/awesomplete/awesomplete.css' %}"/>
+
+{% endblock styles %}
+
+{% block ui_active_tab %}'configuration'{% endblock ui_active_tab %}
+{% block page_description %}{% endblock page_description %}
+
+{% block breadcrumb %}
+    {% include "vouchers/breadcrumb.html" %}
+{% endblock breadcrumb %}
+
+{% block maincontent %}
+
+    {% block content %}
+        <div class="row">
+            <div class="col-md-12">
+                <div class="box box-success">
+                    <div class="box-header with-border">
+                        <h3 class="box-title">{% block form-title %}Enter voucher details{% endblock %}</h3>
+                    </div>
+
+
+                    <form method="post" action="" class="form-horizontal" enctype="multipart/form-data">
+                        {% csrf_token %}
+
+                        <div class="box-body">
+                            {% for field in form %}
+                                <div class="form-group {% if field.errors %}has-error{% endif %}">
+                                    <label class="col-sm-4  col-lg-offset-1 col-lg-2 control-label">
+                                        {{ field.label }}
+                                    </label>
+
+                                    <div class="col-sm-8 col-lg-4">
+                                        {{ field|add_class:'form-control' }}
+                                        {% if field.errors %}
+                                            <span class="help-block">{{ field.errors }}</span>
+                                        {% endif %}
+                                    </div>
+
+
+                                </div>
+                            {% endfor %}
+                        </div><!-- /.box-body -->
+                        <div class="box-footer">
+                            <div class="col-sm-6">
+                                <button type="submit" class="btn btn-block btn-success">{% block save-button %}
+                                    Add{% endblock %}
+                                </button>
+                            </div>
+                            <div class="col-sm-6">
+                                <a href="{% url 'web.views.vouchers' %}"
+                                   class="btn btn-block btn-default">Cancel</a>
+                            </div>
+                        </div><!-- /.box-footer -->
+                    </form>
+                </div>
+
+            </div>
+        </div>
+
+    {% endblock %}
+
+
+{% endblock maincontent %}
+
+{% block scripts %}
+    {{ block.super }}
+
+    <script src="{% static 'AdminLTE/plugins/awesomplete/awesomplete.min.js' %}"></script>
+
+{% endblock scripts %}
\ No newline at end of file
diff --git a/smash/web/templates/vouchers/breadcrumb.html b/smash/web/templates/vouchers/breadcrumb.html
new file mode 100644
index 0000000000000000000000000000000000000000..f71acc808f35010d4240224a41f00db70a830f0d
--- /dev/null
+++ b/smash/web/templates/vouchers/breadcrumb.html
@@ -0,0 +1,2 @@
+<li><a href="{% url 'web.views.appointments' %}"><i class="fa fa-dashboard"></i> Dashboard</a></li>
+<li class="active"><a href="{% url 'web.views.vouchers' %}">Vouchers</a></li>
\ No newline at end of file
diff --git a/smash/web/templates/vouchers/edit.html b/smash/web/templates/vouchers/edit.html
new file mode 100644
index 0000000000000000000000000000000000000000..20e1bea665a6be2c70b8f260a23d8fee1e2b1ba5
--- /dev/null
+++ b/smash/web/templates/vouchers/edit.html
@@ -0,0 +1,10 @@
+{% extends "vouchers/add_edit.html" %}
+
+{% block page_header %}Edit voucher "{{ voucher.number }}"{% endblock page_header %}
+
+{% block title %}{{ block.super }} - Edit voucher "{{ voucher.number }}"{% endblock %}
+
+{% block form-title %}Enter voucher details{% endblock %}
+
+{% block save-button %}Save{% endblock %}
+
diff --git a/smash/web/templates/vouchers/list.html b/smash/web/templates/vouchers/list.html
new file mode 100644
index 0000000000000000000000000000000000000000..7a29b16f449daa5efa1baf27c23dc3d98d17c69e
--- /dev/null
+++ b/smash/web/templates/vouchers/list.html
@@ -0,0 +1,74 @@
+{% extends "_base.html" %}
+{% load static %}
+
+{% block styles %}
+    {{ block.super }}
+    <!-- DataTables -->
+    <link rel="stylesheet" href="{% static 'AdminLTE/plugins/datatables/dataTables.bootstrap.css' %}">
+{% endblock styles %}
+
+{% block ui_active_tab %}'configuration'{% endblock ui_active_tab %}
+{% block page_header %}Vouchers{% endblock page_header %}
+{% block page_description %}{% endblock page_description %}
+
+{% block breadcrumb %}
+    {% include "vouchers/breadcrumb.html" %}
+{% endblock breadcrumb %}
+
+{% block maincontent %}
+
+    <div class="box-body">
+        <table id="table" class="table table-bordered table-striped">
+            <thead>
+            <tr>
+                <th>Number</th>
+                <th>First name</th>
+                <th>Last name</th>
+                <th>Issue date</th>
+                <th>Expiry date</th>
+                <th>Status</th>
+                <th>Use date</th>
+                <th>Partner</th>
+                <th>Feedback</th>
+                <th>Edit</th>
+            </tr>
+            </thead>
+            <tbody>
+            {% for voucher in vouchers %}
+                <tr>
+                    <td>{{ voucher.number }}</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.use_date }}</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>
+    </div>
+{% endblock maincontent %}
+
+{% block scripts %}
+    {{ block.super }}
+
+    <script src="{% static 'AdminLTE/plugins/datatables/jquery.dataTables.min.js' %}"></script>
+    <script src="{% static 'AdminLTE/plugins/datatables/dataTables.bootstrap.min.js' %}"></script>
+
+    <script>
+        $(function () {
+            $('#table').DataTable({
+                "paging": true,
+                "lengthChange": false,
+                "searching": true,
+                "ordering": true,
+                "info": true,
+                "autoWidth": false
+            });
+        });
+    </script>
+{% endblock scripts %}
diff --git a/smash/web/tests/__init__.py b/smash/web/tests/__init__.py
index 4d84e29961f15842b84f3f3c8878073c755a0cbe..692232677b9701a19afaa9b63a85628248f09e5f 100644
--- a/smash/web/tests/__init__.py
+++ b/smash/web/tests/__init__.py
@@ -1,3 +1,4 @@
+import logging
 import os
 from django.conf import settings
 from django.contrib.auth.models import User
@@ -8,6 +9,8 @@ from functions import create_worker
 
 settings.MEDIA_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data')
 
+logger = logging.getLogger(__name__)
+
 
 class LoggedInTestCase(TestCase):
     def setUp(self):
diff --git a/smash/web/tests/api_views/test_subject.py b/smash/web/tests/api_views/test_subject.py
index 2ed3fc0a5721f39de92e27066bbc28ce6c362882..3b4ad50d6f1af4e47c121b1ce4e78f9e7c1a487b 100644
--- a/smash/web/tests/api_views/test_subject.py
+++ b/smash/web/tests/api_views/test_subject.py
@@ -5,14 +5,14 @@ import logging
 
 from django.urls import reverse
 
-from web.tests import LoggedInWithWorkerTestCase
 from web.api_views.subject import get_subjects_order, get_subjects_filtered, serialize_subject
-from web.models import StudySubject, Appointment, Study
+from web.models import StudySubject, Appointment, Study, Worker
 from web.models.constants import GLOBAL_STUDY_ID, SUBJECT_TYPE_CHOICES_PATIENT, SUBJECT_TYPE_CHOICES_CONTROL
 from web.models.study_subject_list import SUBJECT_LIST_GENERIC, SUBJECT_LIST_NO_VISIT, SUBJECT_LIST_REQUIRE_CONTACT, \
     StudySubjectList
+from web.tests import LoggedInWithWorkerTestCase
 from web.tests.functions import create_study_subject, create_get_suffix, create_visit, \
-    create_appointment, create_empty_study_columns, create_contact_attempt, create_flying_team
+    create_appointment, create_empty_study_columns, create_contact_attempt, create_flying_team, create_worker
 from web.views.notifications import get_today_midnight_date
 
 logger = logging.getLogger(__name__)
@@ -384,13 +384,14 @@ class TestSubjectApi(LoggedInWithWorkerTestCase):
 
         self.check_subject_filtered([["some_unknown", "unknown data"]], [subject])
         self.check_subject_filtered([["", ""]], [subject])
-        self.check_subject_filtered([["", None]], [subject])
+        self.check_subject_filtered([[None, None]], [subject])
 
     def test_serialize_subject(self):
         study_subject = self.study_subject
         study_subject.subject.dead = True
         study_subject.flying_team = create_flying_team()
         study_subject.datetime_contact_reminder = get_today_midnight_date()
+        study_subject.health_partner = create_worker()
         create_contact_attempt(subject=study_subject)
         create_visit(subject=study_subject)
 
@@ -534,3 +535,68 @@ class TestSubjectApi(LoggedInWithWorkerTestCase):
         create_contact_attempt(subject=subject)
 
         self.check_subject_ordered("last_contact_attempt", [subject, subject2])
+
+    def test_subjects_ordered_by_information_sent(self):
+        subject = self.study_subject
+        subject.information_sent = False
+        subject.save()
+        subject2 = create_study_subject(2)
+        subject2.information_sent = True
+        subject2.save()
+
+        self.check_subject_ordered("information_sent", [subject, subject2])
+
+    def test_subjects_ordered_by_type(self):
+        subject = self.study_subject
+        subject.type = SUBJECT_TYPE_CHOICES_CONTROL
+        subject.save()
+        subject2 = create_study_subject(2)
+        subject2.type = SUBJECT_TYPE_CHOICES_PATIENT
+        subject2.save()
+
+        self.check_subject_ordered("type", [subject, subject2])
+
+    def test_subjects_ordered_by_social_security_number(self):
+        subject = self.study_subject
+        subject.subject.social_security_number = "01"
+        subject.subject.save()
+        subject2 = create_study_subject(2)
+        subject2.subject.social_security_number = "02"
+        subject2.subject.save()
+
+        self.check_subject_ordered("social_security_number", [subject, subject2])
+
+    def test_subjects_ordered_by_health_partner(self):
+        subject = self.study_subject
+        subject.health_partner = Worker.objects.create(first_name='first1', last_name="name2222", email='jacob@bla', )
+        subject.save()
+        subject2 = create_study_subject(2)
+        subject2.health_partner = Worker.objects.create(first_name='first2', last_name="name1111", email='jacob@bla', )
+        subject2.save()
+
+        self.check_subject_ordered("health_partner_first_name", [subject, subject2])
+        self.check_subject_ordered("health_partner_last_name", [subject2, subject])
+
+    def test_subjects_filter_health_partner_first_name(self):
+        subject = self.study_subject
+        subject.health_partner = Worker.objects.create(first_name='first1', last_name="name2222", email='jacob@bla', )
+        subject.save()
+
+        self.check_subject_filtered([["health_partner_first_name", "first1"]], [subject])
+        self.check_subject_filtered([["health_partner_first_name", "unknown"]], [])
+
+    def test_subjects_filter_health_partner_last_name(self):
+        subject = self.study_subject
+        subject.health_partner = Worker.objects.create(first_name='first1', last_name="name2222", email='jacob@bla', )
+        subject.save()
+
+        self.check_subject_filtered([["health_partner_last_name", "name2222"]], [subject])
+        self.check_subject_filtered([["health_partner_last_name", "unknown"]], [])
+
+    def test_subjects_filter_social_security_number(self):
+        subject = self.study_subject.subject
+        subject.social_security_number = "123"
+        subject.save()
+
+        self.check_subject_filtered([["social_security_number", "12"]], [self.study_subject])
+        self.check_subject_filtered([["social_security_number", "unknown"]], [])
diff --git a/smash/web/tests/forms/test_StudySubjectEditForm.py b/smash/web/tests/forms/test_StudySubjectEditForm.py
index 1e7a5c0919c5b3ed775b23e1cf4bb1b3ab538f59..da51f0a6a8d77fbdf15b5b35a6805ee1a6fbfa77 100644
--- a/smash/web/tests/forms/test_StudySubjectEditForm.py
+++ b/smash/web/tests/forms/test_StudySubjectEditForm.py
@@ -29,6 +29,7 @@ class StudySubjectEditFormTests(LoggedInWithWorkerTestCase):
     def test_validation(self):
         edit_form = StudySubjectEditForm(self.sample_data)
         save_status = edit_form.is_valid()
+        logger.debug(edit_form.errors)
         self.assertTrue(save_status)
 
     def test_validation_with_empty_study(self):
diff --git a/smash/web/tests/forms/test_subject_forms.py b/smash/web/tests/forms/test_subject_forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..fe109455c9595a3b3ee475a27ff23509e41a2e1b
--- /dev/null
+++ b/smash/web/tests/forms/test_subject_forms.py
@@ -0,0 +1,29 @@
+import logging
+
+from web.forms.subject_forms import is_valid_social_security_number
+from web.tests import LoggedInWithWorkerTestCase
+from web.tests.functions import create_subject
+
+logger = logging.getLogger(__name__)
+
+
+class StudySubjectAddFormTests(LoggedInWithWorkerTestCase):
+    def setUp(self):
+        super(StudySubjectAddFormTests, self).setUp()
+        self.subject = create_subject()
+
+    def test_is_valid_social_security_number_too_short(self):
+        self.assertFalse(is_valid_social_security_number("123"))
+
+    def test_is_valid_social_security_number_not_a_number(self):
+        # noinspection SpellCheckingInspection
+        self.assertFalse(is_valid_social_security_number("ABCDEFGHIJKLM"))
+
+    def test_is_valid_social_security_number_invalid(self):
+        self.assertFalse(is_valid_social_security_number("1234567890123"))
+
+    def test_is_valid_social_security_number(self):
+        self.assertTrue(is_valid_social_security_number("1893120105732"))
+
+    def test_is_valid_social_security_number_empty(self):
+        self.assertTrue(is_valid_social_security_number(""))
diff --git a/smash/web/tests/forms/test_voucher_forms.py b/smash/web/tests/forms/test_voucher_forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..64a78eef15b2365f515aecfa030b4da88aaeb166
--- /dev/null
+++ b/smash/web/tests/forms/test_voucher_forms.py
@@ -0,0 +1,73 @@
+import logging
+
+from django.urls import reverse
+
+from web.forms import VoucherForm
+from web.models import Voucher
+from web.models.constants import VOUCHER_STATUS_USED
+from web.tests import LoggedInWithWorkerTestCase
+from web.tests.functions import create_study_subject, create_voucher_type, format_form_field, create_voucher
+
+logger = logging.getLogger(__name__)
+
+
+class VoucherFormTests(LoggedInWithWorkerTestCase):
+    def setUp(self):
+        super(VoucherFormTests, self).setUp()
+
+    def test_auto_generated_use_date(self):
+        voucher_type = create_voucher_type()
+        study_subject = create_study_subject()
+        study_subject.voucher_types.add(voucher_type)
+        create_voucher(study_subject)
+
+        voucher_form = VoucherForm()
+        form_data = {
+            "status": VOUCHER_STATUS_USED,
+            "usage_partner": str(self.worker.id),
+            "voucher_type": voucher_type.id
+        }
+        for key, value in voucher_form.initial.items():
+            form_data[key] = format_form_field(value)
+
+        url = reverse('web.views.voucher_add') + '?study_subject_id=' + str(study_subject.id)
+        response = self.client.post(url, data=form_data)
+        self.assertEqual(response.status_code, 302)
+
+        self.assertEqual(2, Voucher.objects.all().count())
+        self.assertEqual(1, Voucher.objects.filter(use_date__isnull=False).count())
+
+    def test_valid_usage_partner(self):
+        study_subject = create_study_subject()
+        voucher = create_voucher(study_subject)
+        voucher.status = VOUCHER_STATUS_USED
+        voucher.save()
+
+        voucher_form = VoucherForm(instance=voucher)
+
+        form_data = {}
+        for key, value in voucher_form.initial.items():
+            form_data[key] = format_form_field(value)
+        form_data["usage_partner"] = ""
+
+        voucher_form = VoucherForm(instance=voucher, data=form_data)
+
+        self.assertFalse(voucher_form.is_valid())
+        self.assertTrue("usage_partner" in voucher_form.errors)
+
+    def test_valid_status(self):
+        study_subject = create_study_subject()
+        voucher = create_voucher(study_subject)
+        voucher.usage_partner = self.worker
+        voucher.save()
+
+        voucher_form = VoucherForm(instance=voucher)
+
+        form_data = {}
+        for key, value in voucher_form.initial.items():
+            form_data[key] = format_form_field(value)
+
+        voucher_form = VoucherForm(instance=voucher, data=form_data)
+
+        self.assertFalse(voucher_form.is_valid())
+        self.assertTrue("status" in voucher_form.errors)
diff --git a/smash/web/tests/functions.py b/smash/web/tests/functions.py
index 52ba55dfdfa75cfec5161885500b5294244371a4..8261bd6335145cc090f610467b3a7c6523eaeaef 100644
--- a/smash/web/tests/functions.py
+++ b/smash/web/tests/functions.py
@@ -6,10 +6,10 @@ from django.contrib.auth.models import User
 
 from web.models import Location, AppointmentType, StudySubject, Worker, Visit, Appointment, ConfigurationItem, \
     Language, ContactAttempt, FlyingTeam, Availability, Subject, Study, StudyColumns, StudyNotificationParameters, \
-    VoucherType, VoucherTypePrice
+    VoucherType, VoucherTypePrice, Voucher
 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
+    MONDAY_AS_DAY_OF_WEEK, COUNTRY_AFGHANISTAN_ID, VOUCHER_STATUS_NEW
 from web.redcap_connector import RedcapSubject
 from web.views.notifications import get_today_midnight_date
 
@@ -67,7 +67,18 @@ def create_study(name="test"):
     return Study.objects.create(name=name, columns=study_columns, notification_parameters=notification_parameters)
 
 
-def create_empty_notification_parametres():
+def create_voucher(study_subject=None):
+    if study_subject is None:
+        study_subject = create_study_subject()
+    return Voucher.objects.create(number="123456",
+                                  study_subject=study_subject,
+                                  issue_date=get_today_midnight_date(),
+                                  expiry_date=get_today_midnight_date(),
+                                  voucher_type=create_voucher_type(),
+                                  status=VOUCHER_STATUS_NEW)
+
+
+def create_empty_notification_parameters():
     return StudyNotificationParameters.objects.create(
         exceeded_visits_visible=False,
         unfinished_visits_visible=False,
@@ -84,7 +95,7 @@ def create_empty_notification_parametres():
 
 def create_empty_study(name="test"):
     study_columns = create_empty_study_columns()
-    notification_parameters = create_empty_notification_parametres()
+    notification_parameters = create_empty_notification_parameters()
     result = create_study(name)
     result.columns = study_columns
     result.notification_parameters = notification_parameters
@@ -249,6 +260,8 @@ def format_form_field(value):
         return value.strftime('%Y-%m-%d')
     elif isinstance(value, datetime.datetime):
         return value.strftime('%Y-%m-%d %H:%M')
+    elif value is None:
+        return ""
     else:
         return value
 
diff --git a/smash/web/tests/models/test_voucher.py b/smash/web/tests/models/test_voucher.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec5e94316c2bda04dd70bb337498063ff4decca8
--- /dev/null
+++ b/smash/web/tests/models/test_voucher.py
@@ -0,0 +1,15 @@
+import logging
+
+from django.test import TestCase
+
+from web.tests.functions import create_voucher
+
+logger = logging.getLogger(__name__)
+
+
+class VoucherTests(TestCase):
+    def test_to_string(self):
+        voucher = create_voucher()
+
+        self.assertTrue(voucher.number in str(voucher))
+        self.assertTrue(voucher.number in unicode(voucher))
diff --git a/smash/web/tests/models/test_voucher_type.py b/smash/web/tests/models/test_voucher_type.py
new file mode 100644
index 0000000000000000000000000000000000000000..3c47e30bc18f79f2f41ae75cdb6fac9861b6a5f1
--- /dev/null
+++ b/smash/web/tests/models/test_voucher_type.py
@@ -0,0 +1,15 @@
+import logging
+
+from django.test import TestCase
+
+from web.tests.functions import create_voucher_type
+
+logger = logging.getLogger(__name__)
+
+
+class VoucherTypeTests(TestCase):
+    def test_to_string(self):
+        voucher_type = create_voucher_type()
+
+        self.assertTrue(voucher_type.code in str(voucher_type))
+        self.assertTrue(voucher_type.code in unicode(voucher_type))
diff --git a/smash/web/tests/view/test_appointments.py b/smash/web/tests/view/test_appointments.py
index add77509ac5daeefdd5e8e34d23311f7af083c0f..9560cfa02a577b900b541addbf304c98e0506747 100644
--- a/smash/web/tests/view/test_appointments.py
+++ b/smash/web/tests/view/test_appointments.py
@@ -1,6 +1,7 @@
 import datetime
 import logging
 
+from django.core.files.uploadedfile import SimpleUploadedFile
 from django.urls import reverse
 
 from web.forms import AppointmentEditForm, SubjectEditForm, StudySubjectEditForm
@@ -171,17 +172,15 @@ class AppointmentsViewTests(LoggedInTestCase):
     def prepare_form(self, appointment, subject):
         form_appointment = AppointmentEditForm(user=self.user, instance=appointment, prefix="appointment")
         form_study_subject = StudySubjectEditForm(instance=subject, prefix="study-subject")
-        form_subject = SubjectEditForm(instance=subject.subject, prefix="study-subject")
+        form_subject = SubjectEditForm(instance=subject.subject, prefix="subject")
         form_data = {}
         for key, value in form_appointment.initial.items():
-            if value is not None:
-                form_data['appointment-{}'.format(key)] = format_form_field(value)
+            form_data['appointment-{}'.format(key)] = format_form_field(value)
         for key, value in form_study_subject.initial.items():
-            if value is not None:
-                form_data['study-subject-{}'.format(key)] = format_form_field(value)
+            form_data['study-subject-{}'.format(key)] = format_form_field(value)
         for key, value in form_subject.initial.items():
-            if value is not None:
-                form_data['subject-{}'.format(key)] = format_form_field(value)
+            form_data['subject-{}'.format(key)] = format_form_field(value)
+        form_data["study-subject-referral_letter"] = SimpleUploadedFile("file.txt", b"file_content")
         return form_data
 
     def test_subject_flying_team_location(self):
diff --git a/smash/web/tests/view/test_notifications.py b/smash/web/tests/view/test_notifications.py
index e02b515089640176d13e3b57c13c0b7a902a64b8..e84952c33c2207af6c1c65c0e505da4768e0d876 100644
--- a/smash/web/tests/view/test_notifications.py
+++ b/smash/web/tests/view/test_notifications.py
@@ -7,7 +7,7 @@ from web.models import Appointment, Location, AppointmentTypeLink, Study, Visit
 from web.models.constants import GLOBAL_STUDY_ID
 from web.tests import LoggedInTestCase
 from web.tests.functions import create_appointment, create_location, create_worker, create_appointment_type, \
-    create_empty_notification_parametres, create_study_subject, create_visit
+    create_empty_notification_parameters, create_study_subject, create_visit
 from web.views.notifications import \
     get_approaching_visits_for_mail_contact, \
     get_approaching_visits_for_mail_contact_count, \
@@ -118,7 +118,7 @@ class NotificationViewTests(LoggedInTestCase):
 
     def test_get_notifications_with_empty_study_notification(self):
         study = Study.objects.filter(id=GLOBAL_STUDY_ID)[0]
-        study.notification_parameters = create_empty_notification_parametres()
+        study.notification_parameters = create_empty_notification_parameters()
         study.save()
 
         create_worker(self.user)
diff --git a/smash/web/tests/view/test_subjects.py b/smash/web/tests/view/test_subjects.py
index cf9d49777f4ebd58988db0c48c3e8df2d6eb509e..c84a8d6e126f08d90009851e721a4486d3e5cd4c 100644
--- a/smash/web/tests/view/test_subjects.py
+++ b/smash/web/tests/view/test_subjects.py
@@ -1,15 +1,16 @@
 import datetime
 import logging
 
+from django.core.files.uploadedfile import SimpleUploadedFile
 from django.urls import reverse
 
 from web.forms import SubjectAddForm, SubjectEditForm, StudySubjectAddForm, StudySubjectEditForm
-from web.models import MailTemplate, StudySubject
+from web.models import MailTemplate, StudySubject, StudyColumns
 from web.models.constants import SEX_CHOICES_MALE, SUBJECT_TYPE_CHOICES_CONTROL, SUBJECT_TYPE_CHOICES_PATIENT, \
     COUNTRY_AFGHANISTAN_ID, COUNTRY_OTHER_ID, MAIL_TEMPLATE_CONTEXT_SUBJECT
 from web.tests import LoggedInWithWorkerTestCase
 from web.tests.functions import create_study_subject, create_visit, create_appointment, get_test_location, \
-    create_language, get_resource_path, get_test_study
+    create_language, get_resource_path, get_test_study, format_form_field
 from web.views.notifications import get_today_midnight_date
 
 logger = logging.getLogger(__name__)
@@ -75,8 +76,9 @@ class SubjectsViewTests(LoggedInWithWorkerTestCase):
         form_data['subject-dead'] = "True"
         form_data['study_subject-resigned'] = "True"
         form_data['study_subject-resign_reason'] = "doesn't want to participate"
-        response = self.client.post(
-            reverse('web.views.subject_edit', kwargs={'id': self.study_subject.id}), data=form_data)
+
+        url = reverse('web.views.subject_edit', kwargs={'id': self.study_subject.id})
+        response = self.client.post(url, data=form_data)
 
         self.assertEqual(response.status_code, 302)
         updated_study_subject = StudySubject.objects.filter(id=self.study_subject.id)[0]
@@ -98,11 +100,10 @@ class SubjectsViewTests(LoggedInWithWorkerTestCase):
         form_subject = SubjectEditForm(instance=self.study_subject.subject, prefix="subject")
         form_data = {}
         for key, value in form_study_subject.initial.items():
-            if value is not None:
-                form_data['study_subject-{}'.format(key)] = value
+            form_data['study_subject-{}'.format(key)] = format_form_field(value)
         for key, value in form_subject.initial.items():
-            if value is not None:
-                form_data['subject-{}'.format(key)] = value
+            form_data['subject-{}'.format(key)] = format_form_field(value)
+        form_data["study_subject-referral_letter"] = SimpleUploadedFile("file.txt", b"file_content")
         return form_data
 
     def create_add_form_data_for_study_subject(self):
@@ -110,11 +111,9 @@ class SubjectsViewTests(LoggedInWithWorkerTestCase):
         form_subject = SubjectAddForm(prefix="subject")
         form_data = {}
         for key, value in form_study_subject.initial.items():
-            if value is not None:
-                form_data['study_subject-{}'.format(key)] = value
+            form_data['study_subject-{}'.format(key)] = format_form_field(value)
         for key, value in form_subject.initial.items():
-            if value is not None:
-                form_data['subject-{}'.format(key)] = value
+            form_data['subject-{}'.format(key)] = format_form_field(value)
         self.add_valid_form_data_for_subject_add(form_data)
         return form_data
 
@@ -133,6 +132,30 @@ class SubjectsViewTests(LoggedInWithWorkerTestCase):
                          "prefix should start by L" +
                          " as default location prefix is not defined and subject type is control")
 
+    def test_subjects_add_with_referral_letter_file(self):
+        StudyColumns.objects.all().update(referral_letter=True)
+
+        form_data = self.create_add_form_data_for_study_subject()
+
+        form_data["study_subject-type"] = SUBJECT_TYPE_CHOICES_CONTROL
+        form_data["study_subject-default_location"] = get_test_location().id
+        form_data["study_subject-referral_letter"] = SimpleUploadedFile("file.txt", b"file_content")
+        response = self.client.post(reverse('web.views.subject_add'), data=form_data)
+        self.assertEqual(response.status_code, 302)
+        response = self.client.get(response.url)
+        self.assertContains(response, "Subject created")
+
+        subject = StudySubject.objects.all().order_by("-id")[0]
+
+        # check if edit page renders properly
+        response = self.client.get(reverse('web.views.subject_edit', kwargs={'id': subject.id}))
+        self.assertEqual(response.status_code, 200)
+
+        # check if file can be downloaded
+        url = reverse('web.views.uploaded_files') + "?file=" + unicode(subject.referral_letter)
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+
     def add_valid_form_data_for_subject_add(self, form_data):
         form_data["subject-country"] = COUNTRY_AFGHANISTAN_ID
         form_data["subject-first_name"] = "John"
diff --git a/smash/web/tests/view/test_voucher.py b/smash/web/tests/view/test_voucher.py
new file mode 100644
index 0000000000000000000000000000000000000000..583328498a851e367e7ca832f9b581cdc2316bcc
--- /dev/null
+++ b/smash/web/tests/view/test_voucher.py
@@ -0,0 +1,58 @@
+import logging
+
+from django.urls import reverse
+
+from web.forms import VoucherForm
+from web.models import Voucher
+from web.models.constants import VOUCHER_STATUS_NEW
+from web.tests.functions import create_voucher, create_study_subject, format_form_field, create_voucher_type
+from .. import LoggedInTestCase
+
+logger = logging.getLogger(__name__)
+
+
+class VoucherTypeViewTests(LoggedInTestCase):
+    def test_render_add_voucher_request(self):
+        study_subject = create_study_subject()
+        url = reverse('web.views.voucher_add') + "?study_subject_id=" + str(study_subject.id)
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+
+    def test_render_edit_voucher_request(self):
+        voucher = create_voucher()
+        response = self.client.get(reverse('web.views.voucher_edit', kwargs={'pk': voucher.id}))
+        self.assertEqual(response.status_code, 200)
+
+    def test_add_voucher(self):
+        voucher_type = create_voucher_type()
+        study_subject = create_study_subject()
+        study_subject.voucher_types.add(voucher_type)
+        visit_detail_form = VoucherForm()
+        form_data = {
+            "status": VOUCHER_STATUS_NEW,
+            "voucher_type": voucher_type.id
+        }
+        for key, value in visit_detail_form.initial.items():
+            form_data[key] = format_form_field(value)
+
+        url = reverse('web.views.voucher_add') + '?study_subject_id=' + str(study_subject.id)
+        response = self.client.post(url, data=form_data)
+        self.assertEqual(response.status_code, 302)
+
+        self.assertEqual(1, Voucher.objects.all().count())
+
+    def test_edit_voucher(self):
+        voucher = create_voucher()
+        voucher_form = VoucherForm(instance=voucher)
+        form_data = {}
+        for key, value in voucher_form.initial.items():
+            form_data[key] = format_form_field(value)
+
+        form_data["usage_partner"] = ""
+        form_data["use_date"] = ""
+
+        url = reverse('web.views.voucher_edit', kwargs={'pk': voucher.id})
+        response = self.client.post(url, data=form_data)
+        self.assertEqual(response.status_code, 302)
+
+        self.assertEqual(1, Voucher.objects.all().count())
diff --git a/smash/web/tests/view/test_voucher_type_price.py b/smash/web/tests/view/test_voucher_type_price.py
index be90a2fd10abf9ffe14a774be02eee329d1e4fab..5032c40b46ed966049086ef792ba09061bbf78ba 100644
--- a/smash/web/tests/view/test_voucher_type_price.py
+++ b/smash/web/tests/view/test_voucher_type_price.py
@@ -2,7 +2,7 @@ import logging
 
 from django.urls import reverse
 
-from web.forms.forms import VoucherTypePriceForm
+from web.forms import VoucherTypePriceForm
 from web.models import VoucherType, VoucherTypePrice
 from web.tests.functions import create_voucher_type, create_voucher_type_price, format_form_field
 from .. import LoggedInTestCase
@@ -53,6 +53,6 @@ class VoucherTypePriceViewTests(LoggedInTestCase):
 
         url = reverse('web.views.voucher_type_price_edit',
                       kwargs={'pk': voucher_type_price.id, 'voucher_type_id': voucher_type_price.voucher_type.id})
-        response = self.client.post(url, data=form_data)
+        self.client.post(url, data=form_data)
 
         self.assertEqual(90.50, VoucherTypePrice.objects.get(id=voucher_type_price.id).price)
diff --git a/smash/web/urls.py b/smash/web/urls.py
index 7b1137c65d321436acb0ae4f3bc84cef7be342e3..c58aa7bb6a3cebcf9310718f5c7042e1ca58e02f 100644
--- a/smash/web/urls.py
+++ b/smash/web/urls.py
@@ -181,6 +181,14 @@ urlpatterns = [
     url(r'^voucher_types/(?P<voucher_type_id>\d+)/prices/(?P<pk>\d+)/edit$',
         views.voucher_type_price.VoucherTypePriceEditView.as_view(), name='web.views.voucher_type_price_edit'),
 
+    ####################
+    #     VOUCHERS     #
+    ####################
+
+    url(r'^vouchers$', views.voucher.VoucherListView.as_view(), name='web.views.vouchers'),
+    url(r'^vouchers/add$', views.voucher.VoucherCreateView.as_view(), name='web.views.voucher_add'),
+    url(r'^vouchers/(?P<pk>\d+)/edit$', views.voucher.VoucherEditView.as_view(), name='web.views.voucher_edit'),
+
     ####################
     #    STATISTICS    #
     ####################
@@ -201,6 +209,12 @@ urlpatterns = [
 
     url(r'^configuration$', views.configuration_item.configuration_items, name='web.views.configuration'),
 
+    ####################
+    #       FILES      #
+    ####################
+
+    url(r'^files/', views.uploaded_files.download, name='web.views.uploaded_files'),
+
     ####################
     #       AUTH       #
     ####################
diff --git a/smash/web/views/__init__.py b/smash/web/views/__init__.py
index 461b8b16f33c2172552b8b5a59656ef29dff30db..fedbe5fd8b6300d36166e6e9ba058174553a0352 100644
--- a/smash/web/views/__init__.py
+++ b/smash/web/views/__init__.py
@@ -2,6 +2,7 @@
 from django.conf import settings
 from django.contrib.auth.decorators import login_required
 from django.shortcuts import redirect, render
+from django.utils.decorators import method_decorator
 from django.views.generic.base import ContextMixin
 
 from notifications import get_notifications
@@ -53,11 +54,15 @@ def extend_context(params, request):
     return final_params
 
 
+@method_decorator(login_required, name='dispatch')
 class WrappedView(ContextMixin):
     def get_context_data(self, **kwargs):
         context = super(WrappedView, self).get_context_data(**kwargs)
         return extend_context(context, self.request)
 
+    def dispatch(self, *args, **kwargs):
+        return super(WrappedView, self).dispatch(*args, **kwargs)
+
 
 import auth
 import appointment
@@ -73,6 +78,8 @@ import export
 import contact_attempt
 import configuration_item
 import language
+import voucher
 import voucher_type
 import voucher_type_price
 import redcap
+import uploaded_files
diff --git a/smash/web/views/appointment.py b/smash/web/views/appointment.py
index 6d64acd468487ffc1a569241661c309d5b12f5ae..fac01b5a1d1aabe6537fb641e13612734b3a6e76 100644
--- a/smash/web/views/appointment.py
+++ b/smash/web/views/appointment.py
@@ -139,7 +139,6 @@ def appointment_edit(request, id):
         'appointment_form': appointment_form,
         'study_subject_form': study_subject_form,
         'subject_form': subject_form,
-        'id': id,
         'appointment': the_appointment,
         'contact_attempts': contact_attempts,
         'mail_templates': MailTemplate.get_appointment_mail_templates(languages)
diff --git a/smash/web/views/subject.py b/smash/web/views/subject.py
index 5825db2be3767e1075badcd3096e6580cfa20cec..f1c9566a8be16acd252dc146bfcf3c06be5165b9 100644
--- a/smash/web/views/subject.py
+++ b/smash/web/views/subject.py
@@ -2,6 +2,7 @@
 import logging
 
 from django.contrib import messages
+from django.contrib.auth.decorators import login_required
 from django.shortcuts import redirect, get_object_or_404
 
 from ..models.study_subject_list import SUBJECT_LIST_GENERIC, SUBJECT_LIST_NO_VISIT, SUBJECT_LIST_REQUIRE_CONTACT
@@ -21,6 +22,7 @@ def subjects(request):
     return wrap_response(request, 'subjects/index.html', context)
 
 
+@login_required
 def subject_add(request):
     study = Study.objects.filter(id=GLOBAL_STUDY_ID)[0]
     if request.method == 'POST':
diff --git a/smash/web/views/uploaded_files.py b/smash/web/views/uploaded_files.py
new file mode 100644
index 0000000000000000000000000000000000000000..e20a022a77488aabc8e7365352ff8f3b60990a54
--- /dev/null
+++ b/smash/web/views/uploaded_files.py
@@ -0,0 +1,25 @@
+# coding=utf-8
+import logging
+import ntpath
+from wsgiref.util import FileWrapper
+
+from django.contrib.auth.decorators import login_required
+from django.http import HttpResponse
+
+from web.models.constants import FILE_STORAGE
+
+logger = logging.getLogger(__name__)
+
+
+def path_to_filename(path):
+    head, tail = ntpath.split(path)
+    return tail or ntpath.basename(head)
+
+
+@login_required
+def download(request):
+    if request.GET and request.GET.get('file'):
+        path = FILE_STORAGE.location + "/" + request.GET.get('file')
+        response = HttpResponse(FileWrapper(open(path, 'r')), content_type='application/force-download')
+        response['Content-Disposition'] = 'attachment; filename=%s' % path_to_filename(path)
+        return response
diff --git a/smash/web/views/voucher.py b/smash/web/views/voucher.py
new file mode 100644
index 0000000000000000000000000000000000000000..4ca8361cbfb391c347a85db4a4ad38e1bef77266
--- /dev/null
+++ b/smash/web/views/voucher.py
@@ -0,0 +1,66 @@
+# coding=utf-8
+import logging
+
+from django.contrib.messages.views import SuccessMessageMixin
+from django.urls import reverse_lazy
+from django.views.generic import CreateView
+from django.views.generic import ListView
+from django.views.generic import UpdateView
+
+from web.forms import VoucherForm
+from web.models import Voucher, StudySubject
+from web.models.constants import GLOBAL_STUDY_ID
+from . import WrappedView
+
+logger = logging.getLogger(__name__)
+
+
+class VoucherListView(ListView, WrappedView):
+    model = Voucher
+    context_object_name = "vouchers"
+    template_name = 'vouchers/list.html'
+
+
+def voucher_types_for_study_subject(study_subject_id):
+    return StudySubject.objects.get(id=study_subject_id).voucher_types.all()
+
+
+class VoucherCreateView(CreateView, WrappedView):
+    form_class = VoucherForm
+    model = Voucher
+
+    template_name = "vouchers/add.html"
+    success_url = reverse_lazy('web.views.vouchers')
+    success_message = "Voucher type created"
+
+    def form_valid(self, form):
+        form.instance.study_id = GLOBAL_STUDY_ID
+        # noinspection PyUnresolvedReferences
+        form.instance.study_subject_id = self.request.GET.get("study_subject_id", -1)
+        return super(VoucherCreateView, self).form_valid(form)
+
+    def get_success_url(self, **kwargs):
+        # noinspection PyUnresolvedReferences
+        return reverse_lazy('web.views.subject_edit', kwargs={'id': self.request.GET.get("study_subject_id", -1)})
+
+    def get_form_kwargs(self):
+        kwargs = super(VoucherCreateView, self).get_form_kwargs()
+        kwargs['voucher_types'] = voucher_types_for_study_subject(self.request.GET.get("study_subject_id", -1))
+        return kwargs
+
+
+class VoucherEditView(SuccessMessageMixin, UpdateView, WrappedView):
+    form_class = VoucherForm
+    model = Voucher
+
+    success_url = reverse_lazy('web.views.vouchers')
+    success_message = "Voucher saved successfully"
+    template_name = "vouchers/edit.html"
+    context_object_name = "voucher_type"
+
+    def get_success_url(self, **kwargs):
+        # noinspection PyUnresolvedReferences
+        return reverse_lazy('web.views.subject_edit', kwargs={'id': self.get_study_subject_id()})
+
+    def get_study_subject_id(self):
+        return Voucher.objects.get(id=self.kwargs['pk']).study_subject.id
diff --git a/smash/web/views/voucher_type.py b/smash/web/views/voucher_type.py
index 87b13059fb6b2812c22f8aab0e6a0d9e3bb06c29..5cb45eb57245bbf5df0e1163eb274266f30e871e 100644
--- a/smash/web/views/voucher_type.py
+++ b/smash/web/views/voucher_type.py
@@ -4,7 +4,7 @@ from django.views.generic import CreateView
 from django.views.generic import ListView
 from django.views.generic import UpdateView
 
-from web.forms.forms import VoucherTypeForm
+from web.forms import VoucherTypeForm
 from web.models import VoucherType
 from web.models.constants import GLOBAL_STUDY_ID
 from . import WrappedView
diff --git a/smash/web/views/voucher_type_price.py b/smash/web/views/voucher_type_price.py
index f93a045f3d188adc78d2994d5e5e6d135a77f8fc..11f6f4dd43981c64e1bb032accd8b543f763939a 100644
--- a/smash/web/views/voucher_type_price.py
+++ b/smash/web/views/voucher_type_price.py
@@ -5,7 +5,7 @@ from django.urls import reverse_lazy
 from django.views.generic import CreateView
 from django.views.generic import UpdateView
 
-from web.forms.forms import VoucherTypePriceForm
+from web.forms import VoucherTypePriceForm
 from web.models import VoucherTypePrice
 from . import WrappedView
 
diff --git a/smash/web/widgets/__init__.py b/smash/web/widgets/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/smash/web/widgets/secure_file_widget.py b/smash/web/widgets/secure_file_widget.py
new file mode 100644
index 0000000000000000000000000000000000000000..d26d799f1ea97c6414b865633183427ec973bba0
--- /dev/null
+++ b/smash/web/widgets/secure_file_widget.py
@@ -0,0 +1,26 @@
+import logging
+
+from django import forms
+from django.core.urlresolvers import reverse
+from django.utils.safestring import mark_safe
+from django.utils.translation import ugettext_lazy as _
+
+logger = logging.getLogger(__name__)
+
+
+class SecuredFileWidget(forms.FileInput):
+    """A FileField Widget that shows secure file link"""
+
+    def __init__(self, attrs=None):
+        if attrs is None:
+            attrs = {}
+        super(SecuredFileWidget, self).__init__(attrs)
+
+    def render(self, name, value, attrs=None):
+        output = []
+        if value and hasattr(value, "url"):
+            url = reverse('web.views.uploaded_files') + '?file=' + unicode(value)
+            out = u'<a href="{}">{}</a><br />{} '
+            output.append(out.format(url, _(u'Download'), _(u'Change:')))
+        output.append(super(SecuredFileWidget, self).render(name, value, attrs))
+        return mark_safe(u''.join(output))