diff --git a/pms/templates/dashboard.html b/pms/templates/dashboard.html
index 10f0285cc..ca8940c8c 100644
--- a/pms/templates/dashboard.html
+++ b/pms/templates/dashboard.html
@@ -22,6 +22,10 @@
{{dashboard.outcoming_guests}}
Total facturado
€ {% if dashboard.invoiced.total__sum == None %}0.00{% endif %} {{dashboard.invoiced.total__sum|floatformat:2}}
+
+
% Ocupación
+ {{dashboard.occupancy_percentage}}%
+
-{% endblock content%}
\ No newline at end of file
+{% endblock content%}
diff --git a/pms/tests.py b/pms/tests.py
index 7ce503c2d..00e180eef 100644
--- a/pms/tests.py
+++ b/pms/tests.py
@@ -1,3 +1,102 @@
-from django.test import TestCase
+from datetime import date
-# Create your tests here.
+from django.test import TestCase, override_settings
+from django.urls import reverse
+
+from .models import Booking, Customer, Room, Room_type
+
+
+@override_settings(STATICFILES_STORAGE="django.contrib.staticfiles.storage.StaticFilesStorage")
+class DashboardViewTests(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.dashboard_url = reverse("dashboard")
+
+ cls.room_type = Room_type.objects.create(
+ name="Double",
+ price=30,
+ max_guests=2,
+ )
+
+ cls.room_one = Room.objects.create(
+ room_type=cls.room_type,
+ name="Room 1.1",
+ )
+ cls.room_two = Room.objects.create(
+ room_type=cls.room_type,
+ name="Room 1.2",
+ )
+ cls.room_three = Room.objects.create(
+ room_type=cls.room_type,
+ name="Room 1.3",
+ )
+
+ cls.customer = Customer.objects.create(
+ name="John Doe",
+ email="john@example.com",
+ phone="123456789",
+ )
+
+ def create_booking(self, room, state=Booking.NEW, total=60, code=None):
+ return Booking.objects.create(
+ state=state,
+ checkin=date.today(),
+ checkout=date.today(),
+ room=room,
+ guests=1,
+ customer=self.customer,
+ total=total,
+ code=code or f"CODE{Booking.objects.count() + 1:04d}",
+ )
+
+ def test_dashboard_renders_occupancy_widget(self):
+ response = self.client.get(self.dashboard_url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, "% Ocupación")
+ self.assertEqual(response.context["dashboard"]["occupancy_percentage"], 0)
+
+ def test_dashboard_calculates_occupancy_percentage_from_active_bookings(self):
+ self.create_booking(room=self.room_one, state=Booking.NEW)
+ self.create_booking(room=self.room_two, state=Booking.NEW)
+
+ response = self.client.get(self.dashboard_url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertAlmostEqual(
+ response.context["dashboard"]["occupancy_percentage"],
+ 66.7,
+ places=1,
+ )
+ self.assertContains(response, "66.7%")
+
+ def test_dashboard_excludes_deleted_bookings_from_occupancy_percentage(self):
+ self.create_booking(room=self.room_one, state=Booking.NEW)
+ self.create_booking(room=self.room_two, state=Booking.DELETED)
+
+ response = self.client.get(self.dashboard_url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertAlmostEqual(
+ response.context["dashboard"]["occupancy_percentage"],
+ 33.3,
+ places=1,
+ )
+ self.assertContains(response, "33.3%")
+
+ def test_dashboard_excludes_deleted_bookings_from_new_bookings_count(self):
+ self.create_booking(room=self.room_one, state=Booking.NEW)
+ self.create_booking(room=self.room_two, state=Booking.DELETED)
+
+ response = self.client.get(self.dashboard_url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["dashboard"]["new_bookings"], 1)
+
+ def test_dashboard_sets_zero_occupancy_when_there_are_no_rooms(self):
+ Room.objects.all().delete()
+
+ response = self.client.get(self.dashboard_url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["dashboard"]["occupancy_percentage"], 0)
diff --git a/pms/views.py b/pms/views.py
index f38563933..1bb6e4713 100644
--- a/pms/views.py
+++ b/pms/views.py
@@ -1,3 +1,5 @@
+from datetime import date, time, datetime
+
from django.db.models import F, Q, Count, Sum
from django.shortcuts import render, redirect
from django.utils.decorators import method_decorator
@@ -6,7 +8,7 @@
from .form_dates import Ymd
from .forms import *
-from .models import Room
+from .models import Room, Booking
from .reservation_code import generate
@@ -176,7 +178,6 @@ def post(self, request, pk):
class DashboardView(View):
def get(self, request):
- from datetime import date, time, datetime
today = date.today()
# get bookings created today
@@ -185,37 +186,46 @@ def get(self, request):
today_range = (today_min, today_max)
new_bookings = (Booking.objects
.filter(created__range=today_range)
- .values("id")
- ).count()
+ .exclude(state=Booking.DELETED)
+ .count()
+ )
# get incoming guests
incoming = (Booking.objects
.filter(checkin=today)
- .exclude(state="DEL")
- .values("id")
- ).count()
+ .exclude(state=Booking.DELETED)
+ .count()
+ )
# get outcoming guests
outcoming = (Booking.objects
.filter(checkout=today)
- .exclude(state="DEL")
- .values("id")
- ).count()
+ .exclude(state=Booking.DELETED)
+ .count()
+ )
- # get outcoming guests
+ # get invoiced total
invoiced = (Booking.objects
.filter(created__range=today_range)
- .exclude(state="DEL")
+ .exclude(state=Booking.DELETED)
.aggregate(Sum('total'))
)
+ # get occupancy percentage
+ total_rooms = Room.objects.count()
+ confirmed_bookings = Booking.objects.filter(state=Booking.NEW).count()
+ occupancy_percentage = (
+ round((confirmed_bookings / total_rooms) * 100, 1)
+ if total_rooms else 0
+ )
+
# preparing context data
dashboard = {
'new_bookings': new_bookings,
'incoming_guests': incoming,
'outcoming_guests': outcoming,
- 'invoiced': invoiced
-
+ 'invoiced': invoiced,
+ 'occupancy_percentage': occupancy_percentage,
}
context = {
@@ -223,7 +233,6 @@ def get(self, request):
}
return render(request, "dashboard.html", context)
-
class RoomDetailsView(View):
def get(self, request, pk):
# renders room details