diff --git a/smash/web/api_views/appointment.py b/smash/web/api_views/appointment.py
index b8a7ce9375725b076e8747056bedbc21eca82587..a51ddfc058c410f8f65bef590804cf9e793ef35e 100644
--- a/smash/web/api_views/appointment.py
+++ b/smash/web/api_views/appointment.py
@@ -86,7 +86,7 @@ def serialize_appointment(appointment):
     subject_string = ""
     nd_number = screening_number = phone_numbers = appointment_types = None
     if appointment.visit is not None:
-        title = appointment.visit.follow_up_title()
+        title = "Visit " + str(appointment.visit.visit_number)
         subject = appointment.visit.subject
         subject_string = subject.last_name + " " + subject.first_name
         nd_number = subject.nd_number
diff --git a/smash/web/forms.py b/smash/web/forms.py
index 10b925e99020bc6fcb3443316e8df7f4c3b9c00a..e4e4c2bb8b0594b62ea16fba51132eb68282f19b 100644
--- a/smash/web/forms.py
+++ b/smash/web/forms.py
@@ -320,7 +320,7 @@ class VisitDetailForm(ModelForm):
 
     class Meta:
         model = Visit
-        exclude = ['is_finished']
+        exclude = ['is_finished', 'visit_number']
 
 
 class VisitAddForm(ModelForm):
@@ -336,7 +336,7 @@ class VisitAddForm(ModelForm):
 
     class Meta:
         model = Visit
-        exclude = ['is_finished']
+        exclude = ['is_finished', 'visit_number']
 
     def clean(self):
         super(VisitAddForm, self).clean()
diff --git a/smash/web/migrations/0056_visit_visit_number.py b/smash/web/migrations/0056_visit_visit_number.py
new file mode 100644
index 0000000000000000000000000000000000000000..3be1cb193f27909dfbb70c8cddbd9156540ba146
--- /dev/null
+++ b/smash/web/migrations/0056_visit_visit_number.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-10-27 10:22
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('web', '0055_auto_20170925_0905'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='visit',
+            name='visit_number',
+            field=models.IntegerField(default=1, verbose_name=b'Visit number'),
+        ),
+    ]
diff --git a/smash/web/models/visit.py b/smash/web/models/visit.py
index 7554e3984307d09fd55ccaae81c12e8547fc8aef..e11b52d74a7b54d2e837369b839e68a87276be31 100644
--- a/smash/web/models/visit.py
+++ b/smash/web/models/visit.py
@@ -2,6 +2,8 @@
 import datetime
 
 from django.db import models
+from django.db.models.signals import post_save
+from django.dispatch import receiver
 
 from constants import BOOL_CHOICES, SUBJECT_TYPE_CHOICES_CONTROL
 
@@ -33,16 +35,18 @@ class Visit(models.Model):
                                                blank=True,
                                                )
 
+    # this value is automatically computed by signal handled by update_visit_number method
+    visit_number = models.IntegerField(
+        verbose_name='Visit number',
+        default=1
+    )
+
     def __unicode__(self):
         return "%s %s" % (self.subject.first_name, self.subject.last_name)
 
     def __str__(self):
         return "%s %s" % (self.subject.first_name, self.subject.last_name)
 
-    def follow_up_title(self):
-        count = Visit.objects.filter(subject=self.subject, datetime_begin__lt=self.datetime_begin).count()
-        return "Visit " + str(count + 1)
-
     def mark_as_finished(self):
         self.is_finished = True
         self.save()
@@ -59,3 +63,16 @@ class Visit(models.Model):
                 datetime_begin=visit_started + time_to_next_visit,
                 datetime_end=visit_started + time_to_next_visit + datetime.timedelta(days=93)
             )
+
+
+@receiver(post_save, sender=Visit)
+def update_visit_number(sender, instance, **kwargs):
+    visit = instance
+    if visit.subject is not None:
+        count = Visit.objects.filter(subject=visit.subject).filter(datetime_begin__lte=visit.datetime_begin).count()
+        if count != visit.visit_number:
+            visit.visit_number = count
+            visit.save()
+            subject_visits = Visit.objects.filter(subject=visit.subject).all()
+            for subject_visit in subject_visits:
+                update_visit_number(sender, subject_visit)
diff --git a/smash/web/templates/visits/details.html b/smash/web/templates/visits/details.html
index 6953e3a90265956863eb65986af8e6ddac669c95..f6c632b961d953120b79982937a94a509a5cde74 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.follow_up_title }}) {% endblock page_header %}
+{% block page_header %}Details of the visit ({{ visit.visit_number }}) {% endblock page_header %}
 {% block page_description %}{% endblock page_description %}
 
-{% block title %}{{ block.super }} - Details of visit ({{ visit.follow_up_title }}) {% endblock %}
+{% block title %}{{ block.super }} - Details of visit ({{ visit.visit_number }}) {% endblock %}
 
 {% block breadcrumb %}
     {% include "subjects/breadcrumb.html" %}
diff --git a/smash/web/tests/test_model_visit.py b/smash/web/tests/models/test_visit.py
similarity index 93%
rename from smash/web/tests/test_model_visit.py
rename to smash/web/tests/models/test_visit.py
index 2a54bcb98df71668457065eefac420c2f0095071..3b30283777dd33b7638cb0b2fd57860e8ddb029c 100644
--- a/smash/web/tests/test_model_visit.py
+++ b/smash/web/tests/models/test_visit.py
@@ -1,7 +1,6 @@
 from django.test import TestCase
 
-from functions import create_subject
-from functions import create_visit
+from web.tests.functions import create_subject, create_visit
 from web.models import Visit
 
 
diff --git a/smash/web/tests/test_VisitAddForm.py b/smash/web/tests/test_VisitAddForm.py
index 2a2dfe0c6b6d85ca86fb47cd6946d76df0d32189..2efa428bd1f802e54a6b32f654193beadd7982eb 100644
--- a/smash/web/tests/test_VisitAddForm.py
+++ b/smash/web/tests/test_VisitAddForm.py
@@ -7,7 +7,7 @@ from functions import get_test_location
 from web.forms import VisitAddForm
 
 
-class SubjectAddFormTests(TestCase):
+class VisitAddFormTests(TestCase):
     def setUp(self):
         get_test_location()
         self.subject = create_subject()
diff --git a/smash/web/tests/test_view_subjects.py b/smash/web/tests/view/test_subjects.py
similarity index 97%
rename from smash/web/tests/test_view_subjects.py
rename to smash/web/tests/view/test_subjects.py
index ef13ec1fda116890e7c1f8af20f51c17291836d8..8c721780bb9852e2ca929198ca69515e12d90b1b 100644
--- a/smash/web/tests/test_view_subjects.py
+++ b/smash/web/tests/view/test_subjects.py
@@ -5,7 +5,7 @@ from django.test import Client
 from django.test import TestCase
 from django.urls import reverse
 
-from functions import create_subject, create_visit, create_appointment, create_worker, get_test_location
+from web.tests.functions import create_subject, create_visit, create_appointment, create_worker, get_test_location
 from web.forms import SubjectAddForm, SubjectEditForm
 from web.models import Subject
 from web.models.constants import SEX_CHOICES_MALE, SUBJECT_TYPE_CHOICES_CONTROL, SUBJECT_TYPE_CHOICES_PATIENT
@@ -44,7 +44,7 @@ class SubjectsViewTests(TestCase):
         self.assertEqual(response.status_code, 200)
         self.assertFalse("Add visit" in response.content)
 
-    def test_render_subject_visit_details(self):
+    def test_render_subject_visit_details_without_visit(self):
         subject = create_subject()
 
         response = self.client.get(reverse('web.views.subject_visit_details', kwargs={'id': subject.id}))
diff --git a/smash/web/views/export.py b/smash/web/views/export.py
index 359386c2552b4bd0c7e8afd46bb2353beba5917e..b1962c7e66b797f89b141da41266a584e470cdd4 100644
--- a/smash/web/views/export.py
+++ b/smash/web/views/export.py
@@ -91,7 +91,7 @@ def get_appointments_as_array():
     for appointment in appointments:
         if appointment.visit is not None:
             row = [appointment.visit.subject.nd_number, appointment.visit.subject.last_name,
-                   appointment.visit.subject.first_name, appointment.visit.follow_up_title()]
+                   appointment.visit.subject.first_name, appointment.visit.visit_number]
         else:
             row = ["---", "---", "---", "---"]
         for field in appointments_fields:
diff --git a/smash/web/views/subject.py b/smash/web/views/subject.py
index da6675ea4c4a7a796d8394edf639290c3d0f93a7..1bb75f3f71c658840759c6db753f476bdcc23ffb 100644
--- a/smash/web/views/subject.py
+++ b/smash/web/views/subject.py
@@ -90,7 +90,7 @@ def subject_visit_details(request, id):
         appointments = visit.appointment_set.all()
         finished = visit.is_finished
         visit_id = visit.id
-        visit_title = visit.follow_up_title()
+        visit_title = "Visit " + str(visit.visit_number)
         visit_form = VisitDetailForm(instance=visit)
         visits_data.append((visit_form, appointments, finished, visit_id, visit_title))
         if not visit.is_finished: