diff --git a/smash/smash/settings.py b/smash/smash/settings.py
index 14f7a7ff96dc662eadb5b2216029619d0f16cc28..7cd843ec0ac63af7806278d35bb724be47864c62 100644
--- a/smash/smash/settings.py
+++ b/smash/smash/settings.py
@@ -76,7 +76,9 @@ CRON_CLASSES = [
     'web.views.kit.KitRequestEmailSendJob',
     'web.redcap_connector.RedCapRefreshJob',
     'web.views.voucher.ExpireVouchersJob',
-    'web.importer.importer_cron_job.ImporterCronJob'
+    'web.importer.exporter_cron_job.ExporterCronJob',
+    'web.importer.importer_cron_job.SubjectImporterCronJob',
+    'web.importer.importer_cron_job.VisitImporterCronJob'
 ]
 
 # Password validation
diff --git a/smash/web/importer/__init__.py b/smash/web/importer/__init__.py
index 99f8f1dedcee0573a8c4d440f6b98d97e8d973ea..f60887a0d48238dced793d829f0dd82eaabb9cb8 100644
--- a/smash/web/importer/__init__.py
+++ b/smash/web/importer/__init__.py
@@ -1,10 +1,12 @@
 from csv_subject_import_reader import CsvSubjectImportReader
 from csv_tns_subject_import_reader import TnsCsvSubjectImportReader
+from csv_tns_visit_import_reader import TnsCsvVisitImportReader
 from exporter import Exporter
 from exporter_cron_job import ExporterCronJob
 from importer import Importer
-from importer_cron_job import ImporterCronJob
+from importer_cron_job import SubjectImporterCronJob, VisitImporterCronJob
 from subject_import_reader import SubjectImportReader
+from warning_counter import MsgCounterHandler
 
-__all__ = [Importer, SubjectImportReader, CsvSubjectImportReader, ImporterCronJob, Exporter, ExporterCronJob,
-           TnsCsvSubjectImportReader]
+__all__ = [Importer, SubjectImportReader, CsvSubjectImportReader, SubjectImporterCronJob, VisitImporterCronJob,
+           Exporter, ExporterCronJob, TnsCsvSubjectImportReader, TnsCsvVisitImportReader, MsgCounterHandler]
diff --git a/smash/web/importer/csv_tns_visit_import_reader.py b/smash/web/importer/csv_tns_visit_import_reader.py
new file mode 100644
index 0000000000000000000000000000000000000000..185ac5b02436feccf34410aa9aeae068f3e745e5
--- /dev/null
+++ b/smash/web/importer/csv_tns_visit_import_reader.py
@@ -0,0 +1,116 @@
+import csv
+import datetime
+import logging
+
+import pytz
+from django.conf import settings
+
+from warning_counter import MsgCounterHandler
+from web.models import StudySubject, Study, Visit, Appointment, AppointmentType, Location, AppointmentTypeLink
+from web.models.constants import GLOBAL_STUDY_ID
+
+CSV_DATE_FORMAT = "%d/%m/%Y"
+
+logger = logging.getLogger(__name__)
+
+
+class TnsCsvVisitImportReader:
+    def __init__(self):
+        self.study = Study.objects.filter(id=GLOBAL_STUDY_ID)[0]
+        appointment_code = getattr(settings, "IMPORT_APPOINTMENT_TYPE", "SAMPLES")
+
+        appointment_types = AppointmentType.objects.filter(code=appointment_code)
+        if len(appointment_types) > 0:
+            self.appointment_type = appointment_types[0]
+        else:
+            logger.warn("Appointment type does not exist: " + appointment_code)
+            self.appointment_type = None
+        self.problematic_count = 0
+        self.processed_count = 0
+        self.warning_count = 0
+
+    def load_data(self, filename):
+        warning_counter = MsgCounterHandler()
+        logging.getLogger('').addHandler(warning_counter)
+
+        result = []
+        with open(filename) as csv_file:
+            reader = csv.reader(csv_file, delimiter=';')
+            headers = next(reader, None)
+            for row in reader:
+                try:
+                    data = {}
+                    for h, v in zip(headers, row):
+                        data[h] = v
+                    nd_number = data['donor_id']
+                    study_subjects = StudySubject.objects.filter(nd_number=nd_number)
+                    if len(study_subjects) == 0:
+                        raise NotImplementedError
+                    study_subject = study_subjects[0]
+                    visit_number = data['visit_id']
+                    visit_number = int(visit_number) + 1
+                    visits = Visit.objects.filter(subject=study_subject, visit_number=visit_number)
+
+                    if len(visits) > 0:
+                        raise NotImplementedError
+
+                    date = self.extract_date(data['dateofvisit'])
+                    visit = Visit.objects.create(subject=study_subject, visit_number=visit_number, datetime_begin=date,
+                                                 datetime_end=date + datetime.timedelta(days=14))
+                    visit.save()
+                    result.append(visit)
+
+                    location = self.extract_location(data['adressofvisit'])
+
+                    appointment = Appointment.objects.create(visit=visit, length=60, datetime_when=date,
+                                                             location=location)
+                    if self.appointment_type is not None:
+                        AppointmentTypeLink.objects.create(appointment_id=appointment.id,
+                                                           appointment_type=self.appointment_type)
+                    self.processed_count += 1
+                except:
+                    self.problematic_count += 1
+                    logger.warn("Problematic data: " + ';'.join(row))
+
+        if "WARNING" in warning_counter.level2count:
+            self.warning_count = warning_counter.level2count["WARNING"]
+        logging.getLogger('').removeHandler(warning_counter)
+
+        return result
+
+    def extract_date(self, text):
+        # type: (unicode) ->  datetime
+
+        # by default use day after tomorrow
+        result = datetime.datetime.now().replace(hour=9, minute=0) + datetime.timedelta(days=2)
+        try:
+            year = int(text[:4])
+            month = int(text[4:6])
+            day = int(text[6:8])
+            result = result.replace(year=year, month=month, day=day, tzinfo=pytz.UTC)
+        except ValueError:
+            logger.warn("Invalid date: " + text)
+        return result
+
+    def extract_location(self, text):
+        # type: (unicode) ->  Location
+
+        locations = Location.objects.filter(name=text)
+        if len(locations) > 0:
+            return locations[0]
+        else:
+            logger.warn("Location with name does not exist: " + text)
+            return Location.objects.create(name=text)
+
+    def get_summary(self):
+        result = "<p>Number of successfully added appointments: <b>" + str(self.processed_count) + "</b></p>"
+        style = ''
+        if self.problematic_count > 0:
+            style = ' color="red" '
+        result += "<p><font " + style + ">Number of problematic appointments: <b>" + str(
+            self.problematic_count) + "</b></font></p>"
+        style = ''
+        if self.warning_count > 0:
+            style = ' color="brown" '
+        result += "<p><font " + style + ">Number of raised warnings: <b>" + str(self.warning_count) + "</b></font></p>"
+        return result
diff --git a/smash/web/importer/importer_cron_job.py b/smash/web/importer/importer_cron_job.py
index ef0bfd123cc493d004298edd52b3c5b641abae36..670130d4d047e252744cea08f6b026582bdc5f76 100644
--- a/smash/web/importer/importer_cron_job.py
+++ b/smash/web/importer/importer_cron_job.py
@@ -10,6 +10,7 @@ from django.conf import settings
 from django_cron import CronJobBase, Schedule
 
 from csv_tns_subject_import_reader import TnsCsvSubjectImportReader
+from csv_tns_visit_import_reader import TnsCsvVisitImportReader
 from importer import Importer
 from web.models.constants import CRON_JOB_TIMEOUT
 from ..smash_email import EmailSender
@@ -17,14 +18,14 @@ from ..smash_email import EmailSender
 logger = logging.getLogger(__name__)
 
 
-class ImporterCronJob(CronJobBase):
-    RUN_AT_TIMES = getattr(settings, "IMPORT_RUN_AT", ['23:55'])
+class SubjectImporterCronJob(CronJobBase):
+    RUN_AT_TIMES = getattr(settings, "IMPORT_RUN_AT", ['23:45'])
     schedule = Schedule(run_at_times=RUN_AT_TIMES)
-    code = 'web.import_daily_job'  # a unique code
+    code = 'web.import_subjects_daily_job'  # a unique code
 
     @timeout_decorator.timeout(CRON_JOB_TIMEOUT)
     def do(self):
-        email_title = "Daily import"
+        email_title = "Subjects daily import"
         email_recipients = getattr(settings, "DEFAULT_FROM_EMAIL", None)
 
         filename = getattr(settings, "DAILY_IMPORT_FILE", None)
@@ -59,3 +60,47 @@ class ImporterCronJob(CronJobBase):
         new_file = filename + "-" + datetime.datetime.now().strftime("%Y-%m-%d-%H-%M") + ".bac"
         os.rename(filename, new_file)
         return
+
+
+class VisitImporterCronJob(CronJobBase):
+    RUN_AT_TIMES = getattr(settings, "IMPORT_RUN_AT", ['23:55'])
+    schedule = Schedule(run_at_times=RUN_AT_TIMES)
+    code = 'web.import_visits_daily_job'  # a unique code
+
+    @timeout_decorator.timeout(CRON_JOB_TIMEOUT)
+    def do(self):
+        email_title = "Visits daily import"
+        email_recipients = getattr(settings, "DEFAULT_FROM_EMAIL", None)
+
+        filename = getattr(settings, "DAILY_VISIT_IMPORT_FILE", None)
+
+        if filename is None:
+            logger.info("Importing visits skipped. File not defined ")
+            return "import file not defined"
+        logger.info("Importing visits from file: " + filename)
+        if not os.path.isfile(filename):
+            EmailSender().send_email(email_title,
+                                     "<h3><font color='red'>File with imported data is not available in the system: " + filename + "</font></h3>",
+                                     email_recipients)
+            return "import file not found"
+        try:
+            importer = TnsCsvVisitImportReader()
+            importer.load_data(filename)
+            email_body = importer.get_summary()
+            EmailSender().send_email(email_title,
+                                     "<h3>Data was successfully imported from file: " + filename + "</h3>" + email_body,
+                                     email_recipients)
+            self.backup_file(filename)
+            return "import is successful"
+
+        except:
+            tb = traceback.format_exc()
+            EmailSender().send_email(email_title,
+                                     "<h3><font color='red'>There was a problem with importing data from file: " + filename + "</font></h3><pre>" + tb + "</pre>",
+                                     email_recipients)
+            return "import crashed"
+
+    def backup_file(self, filename):
+        new_file = filename + "-" + datetime.datetime.now().strftime("%Y-%m-%d-%H-%M") + ".bac"
+        os.rename(filename, new_file)
+        return
diff --git a/smash/web/migrations/0157_auto_20200414_0909.py b/smash/web/migrations/0157_auto_20200414_0909.py
new file mode 100644
index 0000000000000000000000000000000000000000..a1c759826e330b4299e320882a29413dd3768265
--- /dev/null
+++ b/smash/web/migrations/0157_auto_20200414_0909.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.5 on 2020-04-14 09:09
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('web', '0156_auto_20200406_1207'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='location',
+            name='name',
+            field=models.CharField(max_length=256),
+        ),
+    ]
diff --git a/smash/web/models/location.py b/smash/web/models/location.py
index 5720f26897115e0148138e130988a8b2cbfd9181..217a699d2c0495d7a373ec3b3be68881f6265ccf 100644
--- a/smash/web/models/location.py
+++ b/smash/web/models/location.py
@@ -6,7 +6,7 @@ class Location(models.Model):
     class Meta:
         app_label = 'web'
 
-    name = models.CharField(max_length=20)
+    name = models.CharField(max_length=256)
 
     color = models.CharField(max_length=20,
                              verbose_name='Calendar appointment color',
diff --git a/smash/web/tests/data/tns_import.csv b/smash/web/tests/data/tns_import.csv
deleted file mode 100644
index 588d1661d5f54369ed643d22a56db3c0c1784281..0000000000000000000000000000000000000000
--- a/smash/web/tests/data/tns_import.csv
+++ /dev/null
@@ -1,4 +0,0 @@
-donor_id;firstname;lastname;dateofbirth;phonenr;treatingphysician
-Cov-000001;John;Doe;01/01/1977;555555;Gregory House
-Cov-000002;John2;Doe2;01/02/1977;621000000;Gregory House2
-Cov-000003;John2;Doe2;01/03/1977;691000000;Gregory House3
\ No newline at end of file
diff --git a/smash/web/tests/data/tns_subjects_import.csv b/smash/web/tests/data/tns_subjects_import.csv
new file mode 100644
index 0000000000000000000000000000000000000000..4ae65947d840665eaec52d4320b35be5f57397e1
--- /dev/null
+++ b/smash/web/tests/data/tns_subjects_import.csv
@@ -0,0 +1,4 @@
+donor_id;firstname;lastname;dateofbirth;phonenr;treatingphysician;sig_firstname;sig_lastname;representative
+cov-000111;John;Doe;01/01/1977;555555;Gregory House;John;Doe;Mario Doe
+cov-222333;John2;Doe2;01/02/1977;621000000;Gregory House2;John 2;Doe 2;
+cov-444444;John2;Doe2;01/03/1977;691000000;Gregory House3;John 3;Doe 3;Elsa Doe
\ No newline at end of file
diff --git a/smash/web/tests/data/tns_vouchers_import.csv b/smash/web/tests/data/tns_vouchers_import.csv
new file mode 100644
index 0000000000000000000000000000000000000000..5ea0fe27ede1bcef29aca8082d3fc20c14772ed8
--- /dev/null
+++ b/smash/web/tests/data/tns_vouchers_import.csv
@@ -0,0 +1,4 @@
+donor_id;visit_id;dateofvisit;adressofvisit
+cov-000111;0;20200410;"Laboratoires réunis	23 Route de Diekirch	6555	Bollendorf-Pont"
+cov-222333;0;20200410;PickenDoheem
+cov-444444;0;20200410;"Ketterthill	1-3, rue de la Continentale 	4917	Bascharage"
\ No newline at end of file
diff --git a/smash/web/tests/importer/test_importer_cron_job.py b/smash/web/tests/importer/test_importer_cron_job.py
index 8686ac27053f422180497245e397b76ef893f56d..861a839e47c75317571f0008fad464be61b57419 100644
--- a/smash/web/tests/importer/test_importer_cron_job.py
+++ b/smash/web/tests/importer/test_importer_cron_job.py
@@ -7,7 +7,7 @@ from shutil import copyfile
 from django.conf import settings
 from django.test import TestCase
 
-from web.importer import ImporterCronJob
+from web.importer import SubjectImporterCronJob
 from web.tests.functions import get_resource_path
 
 logger = logging.getLogger(__name__)
@@ -23,7 +23,7 @@ class TestCronJobImporter(TestCase):
     def test_import_without_configuration(self):
         CronJobLog.objects.all().delete()
 
-        job = ImporterCronJob()
+        job = SubjectImporterCronJob()
 
         status = job.do()
 
@@ -31,14 +31,14 @@ class TestCronJobImporter(TestCase):
         self.assertEqual(0, len(mail.outbox))
 
     def test_import(self):
-        filename = get_resource_path('tns_import.csv')
+        filename = get_resource_path('tns_subjects_import.csv')
         new_file, tmp = tempfile.mkstemp()
         copyfile(filename, tmp)
 
         setattr(settings, "DAILY_IMPORT_FILE", tmp)
         CronJobLog.objects.all().delete()
 
-        job = ImporterCronJob()
+        job = SubjectImporterCronJob()
 
         status = job.do()
 
diff --git a/smash/web/tests/importer/test_tns_csv_subject_import_reader.py b/smash/web/tests/importer/test_tns_csv_subject_import_reader.py
index 0bc8e31c6fe699b37b9f12df87e08c61aef54c43..a713f5b1b581491bda3ecebcbeaf859edede4a62 100644
--- a/smash/web/tests/importer/test_tns_csv_subject_import_reader.py
+++ b/smash/web/tests/importer/test_tns_csv_subject_import_reader.py
@@ -10,17 +10,17 @@ from web.tests.functions import get_resource_path
 logger = logging.getLogger(__name__)
 
 
-class TestTnsCsvReader(TestCase):
+class TestTnsCsvSubjectReader(TestCase):
 
     def test_load_data(self):
-        filename = get_resource_path('tns_import.csv')
+        filename = get_resource_path('tns_subjects_import.csv')
         study_subjects = TnsCsvSubjectImportReader().load_data(filename)
         self.assertEqual(3, len(study_subjects))
         study_subject = study_subjects[1]
         self.assertEqual("John2", study_subject.subject.first_name)
         self.assertEqual("Doe2", study_subject.subject.last_name)
-        self.assertEqual("Cov-000002", study_subject.screening_number)
-        self.assertEqual("Cov-000002", study_subject.nd_number)
+        self.assertEqual("cov-222333", study_subject.screening_number)
+        self.assertEqual("cov-222333", study_subject.nd_number)
         self.assertEqual("621000000", study_subject.subject.phone_number)
         self.assertTrue("Gregory House2" in study_subject.comments)
 
diff --git a/smash/web/tests/importer/test_tns_csv_visit_import_reader.py b/smash/web/tests/importer/test_tns_csv_visit_import_reader.py
new file mode 100644
index 0000000000000000000000000000000000000000..d93f37bf78186808d37f7628bb33dfc6f9d21b27
--- /dev/null
+++ b/smash/web/tests/importer/test_tns_csv_visit_import_reader.py
@@ -0,0 +1,52 @@
+# coding=utf-8
+
+import logging
+
+from django.conf import settings
+from django.test import TestCase
+
+from web.importer import TnsCsvVisitImportReader, MsgCounterHandler
+from web.models import Appointment, Visit
+from web.tests.functions import get_resource_path, create_study_subject, create_appointment_type, create_location
+
+logger = logging.getLogger(__name__)
+
+
+class TestTnsCsvSubjectReader(TestCase):
+    def setUp(self):
+        self.warning_counter = MsgCounterHandler()
+        logging.getLogger('').addHandler(self.warning_counter)
+        setattr(settings, "IMPORT_APPOINTMENT_TYPE", "SAMPLE_2")
+        create_appointment_type(code = "SAMPLE_2")
+
+
+    def tearDown(self):
+        setattr(settings, "IMPORT_APPOINTMENT_TYPE", None)
+        logging.getLogger('').removeHandler(self.warning_counter)
+
+    def test_load_data(self):
+        create_study_subject(nd_number='cov-000111')
+        create_study_subject(nd_number='cov-222333')
+        create_study_subject(nd_number='cov-444444')
+
+        create_location(name=u"Laboratoires réunis	23 Route de Diekirch	6555	Bollendorf-Pont")
+        create_location(name=u"PickenDoheem")
+        create_location(name=u"Ketterthill	1-3, rue de la Continentale 	4917	Bascharage")
+
+        filename = get_resource_path('tns_vouchers_import.csv')
+        visits = TnsCsvVisitImportReader().load_data(filename)
+        self.assertEqual(3, len(visits))
+        visit = Visit.objects.filter(id=visits[0].id)[0]
+        self.assertEqual("cov-000111", visit.subject.nd_number)
+        self.assertEqual(1, visit.visit_number)
+
+        appointment = Appointment.objects.filter(visit=visit)[0]
+        self.assertEqual(u"Laboratoires réunis	23 Route de Diekirch	6555	Bollendorf-Pont",
+                         appointment.location.name)
+
+        self.assertEqual(10, appointment.datetime_when.day)
+        self.assertEqual(4, appointment.datetime_when.month)
+        self.assertEqual(2020, appointment.datetime_when.year)
+
+        if "WARNING" in self.warning_counter.level2count:
+            self.assertEquals(0, self.warning_counter.level2count["WARNING"])