diff --git a/smash/db_scripts/create_dummy_data.py b/smash/db_scripts/create_dummy_data.py
index 5f9482d80aa2aeaaa34050963f2809dcfe6f1d28..b6a8ab5324685770bd4c06b381cf03df16cdce2b 100644
--- a/smash/db_scripts/create_dummy_data.py
+++ b/smash/db_scripts/create_dummy_data.py
@@ -432,7 +432,18 @@ class smashProvider(BaseProvider):
             screening_number = self.createSmashScreeningNumber(
                 default_location.prefix)
 
+        virus_test_1 = choice([None, True, False], 1, p=[0.2, 0.3, 0.5])[0]
+        if virus_test_1 is None:
+            virus_test_1_updated = fake.date_between(start_date='-30d', end_date='-4d')
+            #inconclusive results have a date
+            virus_test_1_updated = choice([None, virus_test_1_updated], 1, p=[0.7, 0.3])[0]
+        elif virus_test_1 == True:
+            virus_test_1_updated = fake.date_between(start_date='-30d', end_date='-4d')
+        else:
+            virus_test_1_updated = fake.date_between(start_date='-30d', end_date='-4d')
+
         study_subject, _ = StudySubject.objects.update_or_create(nd_number=nd_number, subject=subject,
+                                                                 virus_test_1=virus_test_1, virus_test_1_updated=virus_test_1_updated,
                                                                  defaults={'default_location': default_location, 'type': type,
                                                                            'screening_number': screening_number, 'study': study})
 
diff --git a/smash/web/admin.py b/smash/web/admin.py
index 91037bb878f4658e2f3142ee72a6b1878ddff938..7d7611b9d71d2767d962fbd1fdd258bb5dbdda0e 100644
--- a/smash/web/admin.py
+++ b/smash/web/admin.py
@@ -1,7 +1,7 @@
 from django.contrib import admin
 
 from models import StudySubject, Item, Room, AppointmentType, Language, Location, Worker, FlyingTeam, Availability, \
-    Holiday, Visit, Appointment
+    Holiday, Visit, Appointment, StudyColumns, StudySubjectList, StudyVisitList, VisitColumns, SubjectColumns
 
 
 class LanguageAdmin(admin.ModelAdmin):
@@ -21,3 +21,8 @@ admin.site.register(FlyingTeam)
 admin.site.register(Availability)
 admin.site.register(Holiday)
 admin.site.register(Appointment)
+admin.site.register(SubjectColumns)
+admin.site.register(StudyColumns)
+admin.site.register(StudySubjectList)
+admin.site.register(StudyVisitList)
+admin.site.register(VisitColumns)
diff --git a/smash/web/api_views/appointment.py b/smash/web/api_views/appointment.py
index 23547b625a624aed307e05739f268b97a1e953cf..8a60dc5949e089291bb09a22f18c1fbf600fb8e5 100644
--- a/smash/web/api_views/appointment.py
+++ b/smash/web/api_views/appointment.py
@@ -5,6 +5,7 @@ from django.http import JsonResponse
 from django.urls import reverse
 from django.utils import timezone
 
+from web.templatetags.filters import display_visit_number
 from web.api_views.serialization_utils import serialize_datetime, location_to_str, flying_team_to_str, add_column, \
     bool_to_yes_no, get_filters_for_data_table_request
 from web.models import Appointment, Study, SubjectColumns, AppointmentColumns, AppointmentList, StudyColumns
@@ -175,7 +176,7 @@ def serialize_appointment(appointment):
     nd_number = screening_number = phone_numbers = appointment_type_names = None
     status = appointment.status
     if appointment.visit is not None:
-        title = "Visit " + str(appointment.visit.visit_number)
+        title = "Visit {}".format(display_visit_number(appointment.visit.visit_number))
         study_subject = appointment.visit.subject
         subject_string = study_subject.subject.last_name + " " + study_subject.subject.first_name
         first_name = study_subject.subject.first_name
diff --git a/smash/web/api_views/serialization_utils.py b/smash/web/api_views/serialization_utils.py
index 5f6cd27120fd039257378a8b3b83cedcc55b1d8e..7df976992e9a300b7472cb68df560fc52ab4dc53 100644
--- a/smash/web/api_views/serialization_utils.py
+++ b/smash/web/api_views/serialization_utils.py
@@ -19,6 +19,17 @@ def bool_to_yes_no_null(val):
         return "NO"
 
 
+def virus_test_to_str(test, date):
+    if test is None and date is not None:
+        return "Inconclusive"
+    if test is None:
+        return "N/A"
+    if test:
+        return "Positive"
+    else:
+        return "Negative"
+
+
 def flying_team_to_str(flying_team):
     result = ""
     if flying_team is not None:
diff --git a/smash/web/api_views/subject.py b/smash/web/api_views/subject.py
index cd7b5ac5dd59429c1457ea3fd30d5f90a4b87c0a..05a96565baf77d46c1c9cf9158f3a107fa9aaffe 100644
--- a/smash/web/api_views/subject.py
+++ b/smash/web/api_views/subject.py
@@ -4,11 +4,13 @@ from django.db.models import Count, Case, When, Min, Max
 from django.db.models import Q
 from django.http import JsonResponse
 from django.urls import reverse
+from web.models import ConfigurationItem
+from distutils.util import strtobool
 
 from web.api_views.serialization_utils import bool_to_yes_no, flying_team_to_str, location_to_str, add_column, \
-    serialize_date, serialize_datetime, get_filters_for_data_table_request, bool_to_yes_no_null
+    serialize_date, serialize_datetime, get_filters_for_data_table_request, virus_test_to_str
 from web.models import StudySubject, Visit, Appointment, Subject, SubjectColumns, StudyColumns, Study, ContactAttempt
-from web.models.constants import SUBJECT_TYPE_CHOICES, GLOBAL_STUDY_ID
+from web.models.constants import SUBJECT_TYPE_CHOICES, GLOBAL_STUDY_ID, VISIT_SHOW_VISIT_NUMBER_FROM_ZERO
 from web.models.study_subject_list import SUBJECT_LIST_GENERIC, SUBJECT_LIST_NO_VISIT, SUBJECT_LIST_REQUIRE_CONTACT, \
     StudySubjectList, SUBJECT_LIST_VOUCHER_EXPIRY
 from web.views import e500_error
@@ -78,23 +80,34 @@ def get_subject_columns(request, subject_list_type):
     add_column(result, "Excluded", "excluded", study_subject_columns, "yes_no_filter", study.columns)
     add_column(result, "Info sent", "information_sent", study_subject_columns, "yes_no_filter", study.columns)
 
-    add_column(result, "Visit 1 virus", "virus_test_1", study_subject_columns, "yes_no_null_filter", study.columns)
-    add_column(result, "Visit 1 virus date", "virus_test_1_updated", study_subject_columns, None, study.columns)
-    add_column(result, "Visit 2 virus", "virus_test_2", study_subject_columns, "yes_no_null_filter", study.columns)
-    add_column(result, "Visit 2 virus date", "virus_test_2_updated", study_subject_columns, None, study.columns)
-    add_column(result, "Visit 3 virus", "virus_test_3", study_subject_columns, "yes_no_null_filter", study.columns)
-    add_column(result, "Visit 3 virus date", "virus_test_3_updated", study_subject_columns, None, study.columns)
-    add_column(result, "Visit 4 virus", "virus_test_4", study_subject_columns, "yes_no_null_filter", study.columns)
-    add_column(result, "Visit 4 virus date", "virus_test_4_updated", study_subject_columns, None, study.columns)
-    add_column(result, "Visit 5 virus", "virus_test_5", study_subject_columns, "yes_no_null_filter", study.columns)
-    add_column(result, "Visit 5 virus date", "virus_test_5_updated", study_subject_columns, None, study.columns)
+    visit_from_zero = ConfigurationItem.objects.get(type=VISIT_SHOW_VISIT_NUMBER_FROM_ZERO).value
+    #True values are y, yes, t, true, on and 1; false values are n, no, f, false, off and 0.
+    if strtobool(visit_from_zero):
+        virus_visit_numbers = range(0, 5)
+        visit_numbers = range(0, study.visits_to_show_in_subject_list + 0)
+    else:
+        virus_visit_numbers = range(1, 5+1)
+        visit_numbers = range(1, study.visits_to_show_in_subject_list + 1)
+
+    for one_based_idx, virus_visit_number in enumerate(virus_visit_numbers, 1):
+        add_column(result, 
+                  'Virus {} RT-PCR'.format(virus_visit_number), 
+                  'virus_test_{}'.format(one_based_idx), #always starts in 1
+                  study_subject_columns,
+                  'yes_no_null_inconclusive_filter', study.columns)
+        add_column(result, 
+                   "Visit {} RT-PCR date".format(virus_visit_number), 
+                   "virus_test_{}_updated".format(one_based_idx), 
+                   study_subject_columns,
+                   "virus_test_date", study.columns)
 
     add_column(result, "Type", "type", study_subject_columns, "type_filter", study.columns)
     add_column(result, "Edit", "edit", None, None, sortable=False)
-    for visit_number in range(1, study.visits_to_show_in_subject_list + 1):
-        visit_key = "visit_" + str(visit_number)
-        add_column(result, "Visit " + str(visit_number), visit_key, None, "visit_filter",
-                   visible_param=study_subject_list.visits)
+    
+    for one_based_idx, visit_number in enumerate(visit_numbers, 1):
+        visit_key = "visit_{}".format(one_based_idx) #always starts in 1
+        add_column(result, "Visit {}".format(visit_number), visit_key, None, 
+                    "visit_filter", visible_param=study_subject_list.visits)
 
     return JsonResponse({"columns": result})
 
@@ -308,27 +321,37 @@ def get_subjects_filtered(subjects_to_be_filtered, filters):
             result = result.filter(endpoint_reached=(value == "true"))
         elif column == "virus_test_1":
             if value == "null":
-                result = result.filter(virus_test_1__isnull=True)
+                result = result.filter(virus_test_1__isnull=True, virus_test_1_updated__isnull=True)
+            elif value == "inconclusive":
+                result = result.filter(virus_test_1__isnull=True, virus_test_1_updated__isnull=False)
             else:
                 result = result.filter(virus_test_1=(value == "true"))
         elif column == "virus_test_2":
             if value == "null":
-                result = result.filter(virus_test_2__isnull=True)
+                result = result.filter(virus_test_2__isnull=True, virus_test_2_updated__isnull=True)
+            elif value == "inconclusive":
+                result = result.filter(virus_test_2__isnull=True, virus_test_2_updated__isnull=False)
             else:
                 result = result.filter(virus_test_2=(value == "true"))
         elif column == "virus_test_3":
             if value == "null":
-                result = result.filter(virus_test_3__isnull=True)
+                result = result.filter(virus_test_3__isnull=True, virus_test_3_updated__isnull=True)
+            elif value == "inconclusive":
+                result = result.filter(virus_test_3__isnull=True, virus_test_3_updated__isnull=False)
             else:
                 result = result.filter(virus_test_3=(value == "true"))
         elif column == "virus_test_4":
             if value == "null":
-                result = result.filter(virus_test_4__isnull=True)
+                result = result.filter(virus_test_4__isnull=True, virus_test_4_updated__isnull=True)
+            elif value == "inconclusive":
+                result = result.filter(virus_test_4__isnull=True, virus_test_4_updated__isnull=False)
             else:
                 result = result.filter(virus_test_4=(value == "true"))
         elif column == "virus_test_5":
             if value == "null":
-                result = result.filter(virus_test_5__isnull=True)
+                result = result.filter(virus_test_5__isnull=True, virus_test_5_updated__isnull=True)
+            elif value == "inconclusive":
+                result = result.filter(virus_test_5__isnull=True, virus_test_5_updated__isnull=False)
             else:
                 result = result.filter(virus_test_5=(value == "true"))
         elif column == "brain_donation_agreement":
@@ -496,11 +519,11 @@ def serialize_subject(study_subject):
         "dead": bool_to_yes_no(study_subject.subject.dead),
         "resigned": bool_to_yes_no(study_subject.resigned),
         "endpoint_reached": bool_to_yes_no(study_subject.endpoint_reached),
-        "virus_test_1": bool_to_yes_no_null(study_subject.virus_test_1),
-        "virus_test_2": bool_to_yes_no_null(study_subject.virus_test_2),
-        "virus_test_3": bool_to_yes_no_null(study_subject.virus_test_3),
-        "virus_test_4": bool_to_yes_no_null(study_subject.virus_test_4),
-        "virus_test_5": bool_to_yes_no_null(study_subject.virus_test_5),
+        "virus_test_1": virus_test_to_str(study_subject.virus_test_1, study_subject.virus_test_1_updated),
+        "virus_test_2": virus_test_to_str(study_subject.virus_test_2, study_subject.virus_test_2_updated),
+        "virus_test_3": virus_test_to_str(study_subject.virus_test_3, study_subject.virus_test_3_updated),
+        "virus_test_4": virus_test_to_str(study_subject.virus_test_4, study_subject.virus_test_4_updated),
+        "virus_test_5": virus_test_to_str(study_subject.virus_test_5, study_subject.virus_test_5_updated),
         "virus_test_1_updated": study_subject.virus_test_1_updated,
         "virus_test_2_updated": study_subject.virus_test_2_updated,
         "virus_test_3_updated": study_subject.virus_test_3_updated,
diff --git a/smash/web/api_views/visit.py b/smash/web/api_views/visit.py
index 59f0eb977ae48eca6fee42d9231f2dee8de4097c..14c7021ffa5a1e07ab9aebad6d6dad88eebdb213 100644
--- a/smash/web/api_views/visit.py
+++ b/smash/web/api_views/visit.py
@@ -2,13 +2,16 @@ import logging
 
 from django.db.models import Q
 from django.http import JsonResponse
+from web.models import ConfigurationItem
+from distutils.util import strtobool
 
 from web.api_views.serialization_utils import bool_to_yes_no, flying_team_to_str, location_to_str, add_column, \
     serialize_date, get_filters_for_data_table_request
 from web.models import AppointmentType, Appointment
 from web.models import SubjectColumns
+from web.templatetags.filters import display_visit_number
 from web.models import Visit, Study, VisitColumns, StudyVisitList, StudyColumns
-from web.models.constants import GLOBAL_STUDY_ID
+from web.models.constants import GLOBAL_STUDY_ID, VISIT_SHOW_VISIT_NUMBER_FROM_ZERO
 from web.models.study_visit_list import VISIT_LIST_GENERIC, VISIT_LIST_EXCEEDED_TIME, VISIT_LIST_UNFINISHED, \
     VISIT_LIST_MISSING_APPOINTMENTS, VISIT_LIST_APPROACHING_WITHOUT_APPOINTMENTS, \
     VISIT_LIST_APPROACHING_FOR_MAIL_CONTACT
@@ -44,7 +47,14 @@ def get_visit_columns(request, visit_list_type):
     add_column(result, "Visit ends", "datetime_end", visit_columns, None)
     add_column(result, "Finished", "is_finished", visit_columns, "yes_no_filter")
     add_column(result, "Post mail sent", "post_mail_sent", visit_columns, "yes_no_filter")
-    add_column(result, "Visit number", "visit_number", visit_columns, "integer_filter")
+    
+    visit_from_zero = ConfigurationItem.objects.get(type=VISIT_SHOW_VISIT_NUMBER_FROM_ZERO).value
+    #True values are y, yes, t, true, on and 1; false values are n, no, f, false, off and 0.
+    if strtobool(visit_from_zero):
+        add_column(result, "Visit number", "display_visit_number", None, "from_zero_integer_filter")
+    else:
+        add_column(result, "Visit number", "visit_number", visit_columns, "integer_filter")
+
     add_column(result, "Appointments in progress", "visible_appointment_types_in_progress", visit_list,
                "appointment_type_filter", sortable=False)
     add_column(result, "Done appointments", "visible_appointment_types_done", visit_list, "appointment_type_filter",
@@ -102,6 +112,8 @@ def get_visits_order(visits_to_be_ordered, order_column, order_direction):
         result = visits_to_be_ordered.order_by(order_direction + 'post_mail_sent')
     elif order_column == "visit_number":
         result = visits_to_be_ordered.order_by(order_direction + 'visit_number')
+    elif order_column == "display_visit_number":
+        result = visits_to_be_ordered.order_by(order_direction + 'visit_number')
     else:
         logger.warn("Unknown sort column: " + str(order_column))
     return result
@@ -153,6 +165,8 @@ def get_visits_filtered(visits_to_be_filtered, filters):
             result = result.filter(post_mail_sent=(value == "true"))
         elif column == "visit_number":
             result = result.filter(visit_number=int(value))
+        elif column == "display_visit_number":
+            result = result.filter(visit_number=int(value)+1)
         elif column == "visible_appointment_types_in_progress":
             result = filter_by_appointment_in_progress(result, value)
         elif column == "visible_appointment_types_done":
@@ -246,4 +260,9 @@ def serialize_visit(visit):
         "visible_appointment_types": appointment_types_to_str(appointment_types),
     }
 
+    visit_from_zero = ConfigurationItem.objects.get(type=VISIT_SHOW_VISIT_NUMBER_FROM_ZERO).value
+    #True values are y, yes, t, true, on and 1; false values are n, no, f, false, off and 0.
+    if strtobool(visit_from_zero):
+        result["display_visit_number"] = display_visit_number(visit.visit_number)
+
     return result
diff --git a/smash/web/forms/forms.py b/smash/web/forms/forms.py
index acead32df6e5e1401a5346ecbec59be00aac8d8d..805f2e72684e1274bc064633bfa71bafd15a0b7d 100644
--- a/smash/web/forms/forms.py
+++ b/smash/web/forms/forms.py
@@ -7,8 +7,10 @@ from django.utils.dates import MONTHS
 
 from web.models import Appointment, AppointmentType, AppointmentTypeLink, \
     Availability, ContactAttempt, FlyingTeam, Holiday, Item, \
-    StudySubject, Room, Worker, Visit, VoucherType, VoucherTypePrice
-from web.models.constants import SUBJECT_TYPE_CHOICES
+    StudySubject, Room, Worker, Visit, VoucherType, VoucherTypePrice, ConfigurationItem
+from web.models.constants import SUBJECT_TYPE_CHOICES, VISIT_SHOW_VISIT_NUMBER_FROM_ZERO
+from distutils.util import strtobool
+from web.templatetags.filters import display_visit_number
 
 """
 Possible redundancy, but if need arises, contents of forms can be easily customized
@@ -127,6 +129,16 @@ class StatisticsForm(Form):
         choices = [(-1, "all")]
         choices.extend(SUBJECT_TYPE_CHOICES.items())
         self.fields['subject_type'] = forms.ChoiceField(choices=choices, initial="-1")
+        visit_from_zero = ConfigurationItem.objects.get(type=VISIT_SHOW_VISIT_NUMBER_FROM_ZERO).value
+        #True values are y, yes, t, true, on and 1; false values are n, no, f, false, off and 0.
+        if strtobool(visit_from_zero):
+            new_choices = []
+            for value, label in visit_choices:
+                if int(value) > 0:
+                    label = display_visit_number(label)
+                new_choices.append((value, label))
+            visit_choices = new_choices
+                    
         self.fields['visit'] = forms.ChoiceField(choices=visit_choices, initial="-1")
 
 
diff --git a/smash/web/forms/study_subject_forms.py b/smash/web/forms/study_subject_forms.py
index 1f75992f6793598ef7d2c760af61af98ab90ab09..bf3a78441409eab3d978470843fc0a2df64cbffc 100644
--- a/smash/web/forms/study_subject_forms.py
+++ b/smash/web/forms/study_subject_forms.py
@@ -1,12 +1,13 @@
 import logging
-import re
 
+from distutils.util import strtobool
 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 ConfigurationItem
 from web.models import StudySubject, Study, StudyColumns, VoucherType, Worker
-from web.models.constants import SCREENING_NUMBER_PREFIXES_FOR_TYPE
+from web.models.constants import SCREENING_NUMBER_PREFIXES_FOR_TYPE, VISIT_SHOW_VISIT_NUMBER_FROM_ZERO
 from web.models.worker_study_role import WORKER_HEALTH_PARTNER
 from web.widgets.secure_file_widget import SecuredFileWidget
 
@@ -14,18 +15,45 @@ logger = logging.getLogger(__name__)
 
 
 class StudySubjectForm(ModelForm):
-    datetime_contact_reminder = forms.DateTimeField(label="Contact on", widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS), required=False)
+    datetime_contact_reminder = forms.DateTimeField(label="Contact on",
+                                                    widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS),
+                                                    required=False)
     referral_letter = forms.FileField(label='Referral letter', widget=SecuredFileWidget(), required=False)
-    voucher_types = forms.ModelMultipleChoiceField(required=False, widget=forms.CheckboxSelectMultiple, queryset=VoucherType.objects.all())
+    voucher_types = forms.ModelMultipleChoiceField(required=False, widget=forms.CheckboxSelectMultiple,
+                                                   queryset=VoucherType.objects.all())
 
     def __init__(self, *args, **kwargs):
         super(StudySubjectForm, self).__init__(*args, **kwargs)
         self.fields['health_partner'].queryset = Worker.get_workers_by_worker_type(
             WORKER_HEALTH_PARTNER)
 
+        visit_from_zero = ConfigurationItem.objects.get(type=VISIT_SHOW_VISIT_NUMBER_FROM_ZERO).value
+        # True values are y, yes, t, true, on and 1; false values are n, no, f, false, off and 0.
+        if strtobool(visit_from_zero):
+            virus_visit_numbers = range(0, 5)
+            for one_based_idx, virus_visit_number in enumerate(virus_visit_numbers, 1):
+                field = 'virus_test_{}'.format(one_based_idx)
+                self.fields[field].label = 'Visit {} RT-PCR'.format(virus_visit_number)
+                date_field = 'virus_test_{}_updated'.format(one_based_idx)
+                self.fields[date_field].label = 'Visit {} RT-PCR date'.format(virus_visit_number)
+        for visit_number in range(1, 6):
+            disable_virus_test_field(self, visit_number)
+            self.update_virus_inconclusive_data(visit_number)
+
+    def update_virus_inconclusive_data(self, visit_number):
+        test_result_column_name = 'virus_test_{}'.format(visit_number)
+        test_date_column_name = 'virus_test_{}_updated'.format(visit_number)
+
+        self.fields[test_result_column_name].widget.choices.append(("Inc", 'Inconclusive'))
+        instance = getattr(self, 'instance', None)
+        if instance and instance.id:
+            test_result = getattr(instance, test_result_column_name)
+            test_date = getattr(instance, test_date_column_name)
+            if test_result is None and test_date is not None:
+                self.initial[test_result_column_name] = "Inc"
 
-class StudySubjectAddForm(StudySubjectForm):
 
+class StudySubjectAddForm(StudySubjectForm):
     class Meta:
         model = StudySubject
         fields = '__all__'
@@ -36,16 +64,8 @@ class StudySubjectAddForm(StudySubjectForm):
         self.study = get_study_from_args(kwargs)
 
         super(StudySubjectAddForm, self).__init__(*args, **kwargs)
-        self.fields['virus_test_1'].widget.attrs['readonly'] = True
-        self.fields['virus_test_2'].widget.attrs['readonly'] = True
-        self.fields['virus_test_3'].widget.attrs['readonly'] = True
-        self.fields['virus_test_4'].widget.attrs['readonly'] = True
-        self.fields['virus_test_5'].widget.attrs['readonly'] = True
-        self.fields['virus_test_1_updated'].widget.attrs['readonly'] = True
-        self.fields['virus_test_2_updated'].widget.attrs['readonly'] = True
-        self.fields['virus_test_3_updated'].widget.attrs['readonly'] = True
-        self.fields['virus_test_4_updated'].widget.attrs['readonly'] = True
-        self.fields['virus_test_5_updated'].widget.attrs['readonly'] = True
+        for visit_number in range(1, 6):
+            disable_virus_test_field(self, visit_number)
 
         prepare_study_subject_fields(fields=self.fields, study=self.study)
 
@@ -104,7 +124,6 @@ def get_new_screening_number(screening_number_prefix):
 
 
 class StudySubjectDetailForm(StudySubjectForm):
-
     class Meta:
         model = StudySubject
         fields = '__all__'
@@ -139,18 +158,6 @@ class StudySubjectEditForm(StudySubjectForm):
         self.fields['resigned'].disabled = was_resigned
         self.fields['endpoint_reached'].disabled = endpoint_was_reached
 
-
-        self.fields['virus_test_1'].widget.attrs['readonly'] = True
-        self.fields['virus_test_2'].widget.attrs['readonly'] = True
-        self.fields['virus_test_3'].widget.attrs['readonly'] = True
-        self.fields['virus_test_4'].widget.attrs['readonly'] = True
-        self.fields['virus_test_5'].widget.attrs['readonly'] = True
-        self.fields['virus_test_1_updated'].widget.attrs['readonly'] = True
-        self.fields['virus_test_2_updated'].widget.attrs['readonly'] = True
-        self.fields['virus_test_3_updated'].widget.attrs['readonly'] = True
-        self.fields['virus_test_4_updated'].widget.attrs['readonly'] = True
-        self.fields['virus_test_5_updated'].widget.attrs['readonly'] = True
-
         prepare_study_subject_fields(fields=self.fields, study=self.study)
 
     def clean(self):
@@ -163,6 +170,14 @@ class StudySubjectEditForm(StudySubjectForm):
         fields = '__all__'
 
 
+def disable_virus_test_field(self, visit_number):
+    test_result_column_name = 'virus_test_{}'.format(visit_number)
+    test_date_column_name = 'virus_test_{}_updated'.format(visit_number)
+    self.fields[test_result_column_name].widget.attrs['readonly'] = True
+    self.fields[test_result_column_name].widget.attrs['disabled'] = True
+    self.fields[test_date_column_name].widget.attrs['readonly'] = True
+
+
 def get_study_from_args(kwargs):
     study = kwargs.pop('study', None)
     if study is None:
@@ -238,7 +253,8 @@ def validate_subject_nd_number(self, cleaned_data):
                 if subjects_from_db:
                     if subjects_from_db[0].screening_number != cleaned_data.get('screening_number', ''):
                         self.add_error('nd_number', "ND number already in use")
-        #else: #empty nd_number is valid
+        # else: #empty nd_number is valid
+
 
 def validate_subject_resign_reason(self, cleaned_data):
     if self.study.columns.resigned and self.study.columns.resign_reason:
diff --git a/smash/web/migrations/0166_auto_20200423_1457.py b/smash/web/migrations/0166_auto_20200423_1457.py
new file mode 100644
index 0000000000000000000000000000000000000000..093f4bb6dd5e998fa900385b6ddd749f6fea3936
--- /dev/null
+++ b/smash/web/migrations/0166_auto_20200423_1457.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.5 on 2020-04-23 14:57
+from __future__ import unicode_literals
+
+from web.models.constants import VISIT_SHOW_VISIT_NUMBER_FROM_ZERO
+from django.db import migrations
+
+
+def create_item(apps, type, value, name):
+    # We can't import the ConfigurationItem model directly as it may be a newer
+    # version than this migration expects. We use the historical version.
+    ConfigurationItem = apps.get_model("web", "ConfigurationItem")
+    item = ConfigurationItem.objects.create()
+    item.type = type
+    item.value = value
+    item.name = name
+    item.save()
+
+def configuration_items(apps, schema_editor):
+    create_item(apps, VISIT_SHOW_VISIT_NUMBER_FROM_ZERO, False,
+                "Should visit numbers be shown starting in 0")
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('web', '0165_configurationitem_email_virus'),
+    ]
+
+    operations = [
+        migrations.RunPython(configuration_items),
+    ]
\ No newline at end of file
diff --git a/smash/web/migrations/0167_auto_20200428_1110.py b/smash/web/migrations/0167_auto_20200428_1110.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea95418d62f1f5c1e0f7261d61771386f094910a
--- /dev/null
+++ b/smash/web/migrations/0167_auto_20200428_1110.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.5 on 2020-04-28 11:10
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('web', '0166_auto_20200423_1457'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='subjectcolumns',
+            name='next_of_keen_address',
+            field=models.BooleanField(default=False, max_length=1, verbose_name=b'Next of kin address'),
+        ),
+        migrations.AlterField(
+            model_name='subjectcolumns',
+            name='next_of_keen_name',
+            field=models.BooleanField(default=False, max_length=1, verbose_name=b'Next of kin'),
+        ),
+        migrations.AlterField(
+            model_name='subjectcolumns',
+            name='next_of_keen_phone',
+            field=models.BooleanField(default=False, max_length=1, verbose_name=b'Next of kin phone'),
+        ),
+        migrations.AlterField(
+            model_name='subject',
+            name='social_security_number',
+            field=models.CharField(blank=True, max_length=50, verbose_name=b'Social security number'),
+        ),
+    ]
diff --git a/smash/web/models/constants.py b/smash/web/models/constants.py
index 361c158e015311d495803b3073978541e978f0c6..51e7a70bcb8dc2f46892003cbe9fff9f67cba961 100644
--- a/smash/web/models/constants.py
+++ b/smash/web/models/constants.py
@@ -43,6 +43,8 @@ CONTACT_TYPES_CHOICES = (
     (CONTACT_TYPES_SMS, 'SMS'),
 )
 
+VISIT_SHOW_VISIT_NUMBER_FROM_ZERO = "VISIT_SHOW_VISIT_NUMBER_FROM_ZERO"
+
 CANCELLED_APPOINTMENT_COLOR_CONFIGURATION_TYPE = "CANCELLED_APPOINTMENT_COLOR"
 NO_SHOW_APPOINTMENT_COLOR_CONFIGURATION_TYPE = "NO_SHOW_APPOINTMENT_COLOR"
 
diff --git a/smash/web/models/subject.py b/smash/web/models/subject.py
index 0f7c3c35441f692e44b17f8df61202b742b69356..ed90375f22a2cf53a3d70c7bea19fa00c21d1588 100644
--- a/smash/web/models/subject.py
+++ b/smash/web/models/subject.py
@@ -34,7 +34,7 @@ class Subject(models.Model):
                                   )
 
     social_security_number = models.CharField(max_length=50,
-                                              verbose_name='Social security_number',
+                                              verbose_name='Social security number',
                                               blank=True,
                                               )
 
diff --git a/smash/web/models/subject_columns.py b/smash/web/models/subject_columns.py
index 04a1869b0787f66eb61e8c76bd858419db16798f..82c4bf3b20df029fcc8e03397f671ef3d303f3df 100644
--- a/smash/web/models/subject_columns.py
+++ b/smash/web/models/subject_columns.py
@@ -86,15 +86,15 @@ class SubjectColumns(models.Model):
 
     next_of_keen_name = models.BooleanField(max_length=1,
                                             default=False,
-                                            verbose_name='Next of keen',
+                                            verbose_name='Next of kin',
                                             )
 
     next_of_keen_phone = models.BooleanField(max_length=1,
                                              default=False,
-                                             verbose_name='Next of keen phone',
+                                             verbose_name='Next of kin phone',
                                              )
 
     next_of_keen_address = models.BooleanField(max_length=1,
                                                default=False,
-                                               verbose_name='Next of keen address',
+                                               verbose_name='Next of kin address',
                                                )
diff --git a/smash/web/static/js/smash.js b/smash/web/static/js/smash.js
index fecda37d4b7560d1b16fd9ee768119189ea9138c..e49418ae8988a3bb99154b18b14fb6a3863f184a 100644
--- a/smash/web/static/js/smash.js
+++ b/smash/web/static/js/smash.js
@@ -303,6 +303,23 @@ function createTable(params) {
     $(tableElement).find('tfoot div[name="yes_no_null_filter"]').each(function () {
         $(this).html('<select style="width:60px" ><option value selected="selected">---</option><option value="true">YES</option><option value="false">NO</option><option value="null">N/A</option></select>');
     });
+    $(tableElement).find('tfoot div[name="yes_no_null_inconclusive_filter"]').each(function () {
+        $(this).html('<select style="width:100px" ><option value selected="selected">---</option><option value="true">Positive</option><option value="false">Negative</option><option value="null">N/A</option><option value="inconclusive">Inconclusive</option></select>');
+    });
+
+    //make columns of virus test date wider
+    $(tableElement).find('tfoot div[name="virus_test_date"]').each(function () {
+        $(this).css('width', '100px');
+        $(this).text('');
+    });
+
+    //replace the hyphen with non breaking space hyphen to ensure the column names are more readable
+    $(tableElement).find('th:contains("RT-PCR")').each(function(){
+        non_breaking_hyphen = $.parseHTML('&#8209;');
+        var text = $(this).text().replace('-', $(non_breaking_hyphen).text());
+        $(this).text(text);
+    });
+
     $(tableElement).find('tfoot div[name="integer_filter"]').each(function () {
         var options = '<option value selected="selected">---</option>';
         for (var i = 1; i <= 8; i++) {
@@ -311,6 +328,14 @@ function createTable(params) {
         $(this).html('<select style="width:60px" >' + options + '</select>');
     });
 
+    $(tableElement).find('tfoot div[name="from_zero_integer_filter"]').each(function () {
+        var options = '<option value selected="selected">---</option>';
+        for (var i = 0; i < 8; i++) {
+            options += '<option value="' + i + '">' + i + '</option>';
+        }
+        $(this).html('<select style="width:60px" >' + options + '</select>');
+    });
+
     $(tableElement).find('tfoot div[name="voucher_status_filter"]').each(function () {
         $(this).html('<select style="width:60px" ><option value selected="selected">---</option>' +
             '<option value="NEW">NEW</option>' +
diff --git a/smash/web/templates/subjects/visit_details.html b/smash/web/templates/subjects/visit_details.html
index d7d2a88a324566d3769bed54f6a17ce17c510145..7dc6a54968d2f62947b2b46c53a19db1448937df 100644
--- a/smash/web/templates/subjects/visit_details.html
+++ b/smash/web/templates/subjects/visit_details.html
@@ -10,7 +10,7 @@
 
 {% block ui_active_tab %}'subjects'{% endblock ui_active_tab %}
 {% block page_header %}List of visits{% endblock page_header %}
-{% block page_description %}{% endblock page_description %}
+{% block page_description %}Latest visit first{% endblock page_description %}
 
 {% block title %}{{ block.super }} - List of subject's visits {% endblock %}
 
@@ -36,25 +36,25 @@
                     {% for element in display %}
                         <div class="box box-widget widget-user-2">
                             <div class="widget-user-header bg-green">
-                                <h3 class="widget-user-username">{{ element.4 }}</h3>
+                                <h3 class="widget-user-username">Visit {{ element.visit_number | display_visit_number }}</h3>
                                 <h5 class="widget-user-desc">
-                                    {% if element.2 %}(Finished)
+                                    {% if element.finished %}(Finished)
                                     {% else %}(Not finished)
                                     {% endif %}
-                                    <a href="{% url 'web.views.visit_details' element.3 %}"><font color="#D3D3D3">Details
+                                    <a href="{% url 'web.views.visit_details' element.visit_id %}"><font color="#D3D3D3">Details
                                         >>></font></a>
                                 </h5>
                             </div>
                             <div class="box-footer">
-                                {% if not element.2 %}
+                                {% if not element.finished %}
                                     <div>
-                                        <a href="{% url 'web.views.appointment_add' element.3 %}" class="btn btn-app">
+                                        <a href="{% url 'web.views.appointment_add' element.visit_id %}" class="btn btn-app">
                                             <i class="fa fa-plus"></i>
                                             Add new appointment
                                         </a>
                                     </div>
                                 {% endif %}
-                                {% for field in element.0 %}
+                                {% for field in element.visit_form %}
                                     {% if not field|is_checkbox %}
                                         <div class="col-md-6 form-group  {% if field.errors %}has-error{% endif %}  {% if field|is_checkbox %}multi-checkboxes{% endif %}">
                                             <label class="col-sm-4 control-label">{{ field.label }}</label>
@@ -67,7 +67,7 @@
                                                 <span class="help-block">{{ field.errors }}</span>{% endif %}
                                         </div>
                                     {% else %}
-                                        {% include 'includes/visit_appointment_types_field.html' with field=field appointments=element.1 readonly="true" %}
+                                        {% include 'includes/visit_appointment_types_field.html' with field=field appointments=element.appointments readonly="true" %}
                                     {% endif %}
                                 {% endfor %}
                             </div>
@@ -79,7 +79,7 @@
                                 <h3 class="widget-user-username">Visit's appointments</h3>
                             </div>
                             <div class="box-footer">
-                                {% if element.1 %}
+                                {% if element.appointments %}
                                     <table id="table" class="table table-bordered table-striped">
                                         <thead>
                                         <tr>
@@ -93,7 +93,7 @@
                                         </tr>
                                         </thead>
                                         <tbody>
-                                        {% for app in element.1 %}
+                                        {% for app in element.appointments %}
                                             <tr>
                                                 <td>{{ forloop.counter }}</td>
                                                 <td style="background-color:{{ app.color }} !important">
diff --git a/smash/web/templates/visits/details.html b/smash/web/templates/visits/details.html
index 1b5d9f6ee51346092cfb3d77091ab85766754280..6935b7d64949765f1b0b35e436c4640ff5f8f88e 100644
--- a/smash/web/templates/visits/details.html
+++ b/smash/web/templates/visits/details.html
@@ -11,10 +11,10 @@
 {% endblock styles %}
 
 {% block ui_active_tab %}'visits'{% endblock ui_active_tab %}
-{% block page_header %}Details of the visit ({{ visit.visit_number }}) {% endblock page_header %}
+{% block page_header %}Details of the visit ({{ visit.visit_number | display_visit_number }}) {% endblock page_header %}
 {% block page_description %}{% endblock page_description %}
 
-{% block title %}{{ block.super }} - Details of visit ({{ visit.visit_number }}) {% endblock %}
+{% block title %}{{ block.super }} - Details of visit ({{ visit.visit_number | display_visit_number }}) {% endblock %}
 
 {% block breadcrumb %}
     {% include "subjects/breadcrumb.html" %}
diff --git a/smash/web/templatetags/filters.py b/smash/web/templatetags/filters.py
index ea2f0e67d5cab17040141248e6c7ffab7766852d..218e34bc50f142b794a28ba7cf932b9de12c600a 100644
--- a/smash/web/templatetags/filters.py
+++ b/smash/web/templatetags/filters.py
@@ -3,6 +3,9 @@ from django import template
 from django.forms import CheckboxSelectMultiple, CheckboxInput
 from django.utils.safestring import mark_safe
 import datetime
+from web.models import ConfigurationItem
+from web.models.constants import VISIT_SHOW_VISIT_NUMBER_FROM_ZERO
+from distutils.util import strtobool
 
 register = template.Library()
 
@@ -46,3 +49,13 @@ def render_appointments(statistics, appointment_type):
 def timestamp(value):
     epoch = datetime.datetime.utcfromtimestamp(0)
     return (value.replace(tzinfo=None) - epoch).total_seconds()
+
+
+@register.filter(name='display_visit_number')
+def display_visit_number(visit_number):
+    visit_from_zero = ConfigurationItem.objects.get(type=VISIT_SHOW_VISIT_NUMBER_FROM_ZERO).value
+    #True values are y, yes, t, true, on and 1; false values are n, no, f, false, off and 0.
+    if strtobool(visit_from_zero):
+        return (visit_number - 1)
+    else:
+        return visit_number
\ No newline at end of file
diff --git a/smash/web/views/export.py b/smash/web/views/export.py
index 1cb6271207a31855a0199c62945cd654e336849a..9130b0fa15804a1dcbaf7bd983e3f5060c0ef3c3 100644
--- a/smash/web/views/export.py
+++ b/smash/web/views/export.py
@@ -7,7 +7,11 @@ from django.http import HttpResponse
 from notifications import get_today_midnight_date
 from web.decorators import PermissionDecorator
 from . import e500_error, wrap_response
-from ..models import Subject, StudySubject, Appointment
+from ..models import Subject, StudySubject, Appointment, ConfigurationItem
+from web.models.constants import VISIT_SHOW_VISIT_NUMBER_FROM_ZERO
+from distutils.util import strtobool
+from web.templatetags.filters import display_visit_number
+import re
 
 
 @PermissionDecorator('export_subjects', 'subject')
@@ -86,12 +90,23 @@ def filter_fields_from_selected_fields(fields, selected_fields):
 
 
 def get_default_subject_fields():
+    visit_from_zero = ConfigurationItem.objects.get(type=VISIT_SHOW_VISIT_NUMBER_FROM_ZERO).value
+    visit_from_zero = strtobool(visit_from_zero)
     subject_fields = []
     for field in Subject._meta.fields:
         if field.name.upper() != "ID":
             subject_fields.append(field)
     for field in StudySubject._meta.fields:
         if field.name.upper() != "ID" and field.name.upper() != "SUBJECT":
+            if visit_from_zero:
+                match = re.match(r'^virus_test_(\d*)$', field.name)
+                if match:
+                    number = int(match.groups()[0])
+                    field.verbose_name = 'Virus {} RT-PCR'.format(display_visit_number(number))
+                match = re.match(r'^virus_test_(\d*)_updated$', field.name)
+                if match:
+                    number = int(match.groups()[0])
+                    field.verbose_name = 'Virus {} RT-PCR date'.format(display_visit_number(number))
             subject_fields.append(field)
     subject_fields.append(DROP_OUT_FIELD)
     return subject_fields
diff --git a/smash/web/views/subject.py b/smash/web/views/subject.py
index ef65d5f486e0aa018a762fa8deb0091a09fdf72a..1583412ae89e5cb740e9a6fc3aab8581fdf71ee1 100644
--- a/smash/web/views/subject.py
+++ b/smash/web/views/subject.py
@@ -152,12 +152,14 @@ def subject_visit_details(request, id):
     visits_data = []
     allow_add_visit = True
     for visit in visits:
-        appointments = visit.appointment_set.all()
-        finished = visit.is_finished
-        visit_id = visit.id
-        visit_title = "Visit " + str(visit.visit_number)
-        visit_form = VisitDetailForm(instance=visit)
-        visits_data.append((visit_form, appointments, finished, visit_id, visit_title))
+        data = {
+            'visit_form': VisitDetailForm(instance=visit),
+            'appointments': visit.appointment_set.all(),
+            'finished': visit.is_finished,
+            'visit_id': visit.id,
+            'visit_number': visit.visit_number
+        }
+        visits_data.append(data)
         if not visit.is_finished:
             allow_add_visit = False
         if not study_subject_to_be_viewed.can_schedule():