diff --git a/pms/forms.py b/pms/forms.py index f1bc68d08..c48be65ca 100644 --- a/pms/forms.py +++ b/pms/forms.py @@ -2,7 +2,7 @@ from django import forms from django.forms import ModelForm -from .models import Booking, Customer +from .models import Booking, Customer, Room class RoomSearchForm(ModelForm): @@ -56,3 +56,15 @@ class Meta: 'total': forms.HiddenInput(), 'state': forms.HiddenInput(), } + + +class RoomFilterForm(forms.Form): + search = forms.CharField( + required=False, + label='Buscar habitación', + max_length=100, + widget=forms.TextInput(attrs={ + 'class': 'form-control', + 'placeholder': 'Ej: Room 1' + }) + ) diff --git a/pms/templates/rooms.html b/pms/templates/rooms.html index c30929f1f..cfb9d78c2 100644 --- a/pms/templates/rooms.html +++ b/pms/templates/rooms.html @@ -2,18 +2,50 @@ {% block content %}

Habitaciones del hotel

-{% for room in rooms%} -
-
-
- {{room.name}} ({{room.room_type__name}}) -
-
- Ver detalles + + +
+
+
+
+
+ {{ form.search }} +
+
+ + {% if search_query %} + Limpiar + {% endif %} +
+
+
+ {% if search_query %} +
+ Buscando: "{{ search_query }}" - {{ rooms|length }} resultado(s) encontrado(s) +
+ {% endif %} +
+
+ + +{% if rooms %} + {% for room in rooms%} +
+
+
+ {{room.name}} ({{room.room_type__name}}) +
+ +
- -
-{% endfor %} + {% endfor %} +{% else %} +
+ No se encontraron habitaciones{% if search_query %} que coincidan con "{{ search_query }}"{% endif %}. +
+{% endif %} {% endblock content%} diff --git a/pms/tests.py b/pms/tests.py index 7ce503c2d..1afb790d2 100644 --- a/pms/tests.py +++ b/pms/tests.py @@ -1,3 +1,138 @@ -from django.test import TestCase +from django.test import TestCase, Client +from django.urls import reverse +from .models import Room, Room_type +from .forms import RoomFilterForm -# Create your tests here. + +class RoomFilterFormTests(TestCase): + """Test cases for RoomFilterForm""" + + def test_form_field_search_is_optional(self): + """Test that search field is not required""" + form = RoomFilterForm(data={'search': ''}) + self.assertTrue(form.is_valid()) + + def test_form_with_valid_search_query(self): + """Test form with valid search query""" + form = RoomFilterForm(data={'search': 'Room 1'}) + self.assertTrue(form.is_valid()) + self.assertEqual(form.cleaned_data['search'], 'Room 1') + + +class RoomsViewFilterTests(TestCase): + """Test cases for RoomsView filtering functionality""" + + @classmethod + def setUpTestData(cls): + """Set up test data for all test methods""" + # Create room types + cls.room_type_individual = Room_type.objects.create( + name='Individual', + price=20.0, + max_guests=1 + ) + cls.room_type_doble = Room_type.objects.create( + name='Doble', + price=30.0, + max_guests=2 + ) + + # Create test rooms + cls.room_1_1 = Room.objects.create( + name='Room 1.1', + description='Habitación individual 1.1', + room_type=cls.room_type_individual + ) + cls.room_1_2 = Room.objects.create( + name='Room 1.2', + description='Habitación individual 1.2', + room_type=cls.room_type_individual + ) + cls.room_2_1 = Room.objects.create( + name='Room 2.1', + description='Habitación doble 2.1', + room_type=cls.room_type_doble + ) + cls.room_2_2 = Room.objects.create( + name='Room 2.2', + description='Habitación doble 2.2', + room_type=cls.room_type_doble + ) + cls.room_3_1 = Room.objects.create( + name='Room 3.1', + description='Habitación individual 3.1', + room_type=cls.room_type_individual + ) + + def setUp(self): + """Set up test client""" + self.client = Client() + self.url = reverse('rooms') + + def test_rooms_view_without_filter_shows_all_rooms(self): + """Test that without filter all rooms are displayed""" + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 5) + self.assertContains(response, 'Room 1.1') + self.assertContains(response, 'Room 2.1') + self.assertContains(response, 'Room 3.1') + + def test_rooms_view_with_exact_search(self): + """Test exact room name search""" + response = self.client.get(self.url, {'search': 'Room 1.1'}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 1) + self.assertContains(response, 'Room 1.1') + self.assertNotContains(response, 'Room 2.1') + + def test_rooms_view_with_partial_search(self): + """Test partial room name search (e.g., 'Room 1' returns 'Room 1.1' and 'Room 1.2')""" + response = self.client.get(self.url, {'search': 'Room 1'}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 2) + self.assertContains(response, 'Room 1.1') + self.assertContains(response, 'Room 1.2') + self.assertNotContains(response, 'Room 2.1') + + def test_rooms_view_with_no_results(self): + """Test search with no matching rooms""" + response = self.client.get(self.url, {'search': 'NonExistent'}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 0) + self.assertContains(response, 'No se encontraron habitaciones') + + def test_rooms_view_search_is_case_insensitive(self): + """Test that search is case insensitive""" + response = self.client.get(self.url, {'search': 'room 1'}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 2) + self.assertContains(response, 'Room 1.1') + self.assertContains(response, 'Room 1.2') + + def test_rooms_view_search_with_whitespace(self): + """Test that search handles whitespace correctly""" + response = self.client.get(self.url, {'search': ' Room 2 '}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 2) + self.assertContains(response, 'Room 2.1') + self.assertContains(response, 'Room 2.2') + + def test_rooms_view_empty_search_shows_all_rooms(self): + """Test that empty search parameter shows all rooms""" + response = self.client.get(self.url, {'search': ''}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.context['rooms']), 5) + + def test_rooms_view_contains_search_form(self): + """Test that the view includes the search form""" + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + self.assertIn('form', response.context) + self.assertIsInstance(response.context['form'], RoomFilterForm) + + def test_rooms_view_preserves_search_query_in_context(self): + """Test that search query is preserved in context""" + response = self.client.get(self.url, {'search': 'Room 1'}) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['search_query'], 'Room 1') diff --git a/pms/views.py b/pms/views.py index f38563933..b25464275 100644 --- a/pms/views.py +++ b/pms/views.py @@ -238,9 +238,21 @@ def get(self, request, pk): class RoomsView(View): def get(self, request): - # renders a list of rooms - rooms = Room.objects.all().values("name", "room_type__name", "id") + # Get search query from GET parameters (received from the search form in the template) + search_query = request.GET.get('search', '').strip() + + # Filter rooms by name if search query exists + if search_query: + rooms = Room.objects.filter(name__icontains=search_query).values("name", "room_type__name", "id") + else: + rooms = Room.objects.all().values("name", "room_type__name", "id") + + # Initialize form with search query to keep it in the input field + form = RoomFilterForm(initial={'search': search_query}) + context = { - 'rooms': rooms + 'rooms': rooms, + 'form': form, + 'search_query': search_query } return render(request, "rooms.html", context)