From f20ffaa0af76625d6025fc9cbd857b3713ed3c9a Mon Sep 17 00:00:00 2001 From: wild_fly Date: Sat, 11 Apr 2026 20:19:36 +0200 Subject: [PATCH 1/3] add booking date editing with availability validation --- .idea/testbookingengine.iml | 6 +- pms/forms.py | 19 ++++++ pms/templates/edit_booking_dates.html | 28 ++++++++ pms/templates/home.html | 2 +- pms/tests.py | 96 ++++++++++++++++++++++++++- pms/urls.py | 1 + pms/views.py | 39 +++++++++++ 7 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 pms/templates/edit_booking_dates.html diff --git a/.idea/testbookingengine.iml b/.idea/testbookingengine.iml index 690497d10..766f607d0 100644 --- a/.idea/testbookingengine.iml +++ b/.idea/testbookingengine.iml @@ -13,8 +13,10 @@ - - + + + + diff --git a/pms/forms.py b/pms/forms.py index f1bc68d08..e211ccdaa 100644 --- a/pms/forms.py +++ b/pms/forms.py @@ -56,3 +56,22 @@ class Meta: 'total': forms.HiddenInput(), 'state': forms.HiddenInput(), } + +class BookingDatesForm(forms.ModelForm): + class Meta: + model = Booking + fields = ["checkin", "checkout"] + + # Receives the check-in and check-out dates for a booking. + # Checks that the selected date range is valid. + # Returns cleaned data when the form is valid, or errors if validation fails. + + def clean(self): + cleaned_data = super().clean() + checkin = cleaned_data.get("checkin") + checkout = cleaned_data.get("checkout") + + if checkin and checkout and checkout <= checkin: + raise forms.ValidationError("La fecha de salida debe ser posterior a la fecha de entrada.") + + return cleaned_data \ No newline at end of file diff --git a/pms/templates/edit_booking_dates.html b/pms/templates/edit_booking_dates.html new file mode 100644 index 000000000..4bcf71e6c --- /dev/null +++ b/pms/templates/edit_booking_dates.html @@ -0,0 +1,28 @@ +{% extends "main.html" %} + +{% block content %} +

Editar fechas de la reserva

+ +
+

Habitación: {{ booking.room.name }}

+ +
+ {% csrf_token %} + {{ form.non_field_errors }} + +
+ {{ form.checkin.label_tag }} + {{ form.checkin }} + {{ form.checkin.errors }} +
+ +
+ {{ form.checkout.label_tag }} + {{ form.checkout }} + {{ form.checkout.errors }} +
+ + +
+
+{% endblock content %} \ No newline at end of file diff --git a/pms/templates/home.html b/pms/templates/home.html index 1e61b8024..8409e134e 100644 --- a/pms/templates/home.html +++ b/pms/templates/home.html @@ -64,8 +64,8 @@

Reservas Realizadas

diff --git a/pms/tests.py b/pms/tests.py index 7ce503c2d..0c66233ad 100644 --- a/pms/tests.py +++ b/pms/tests.py @@ -1,3 +1,97 @@ -from django.test import TestCase +from datetime import date + +from django.test import TestCase, override_settings +from django.urls import reverse + +from pms.models import Booking, Customer, Room, Room_type # Create your tests here. +@override_settings(STATICFILES_STORAGE="django.contrib.staticfiles.storage.StaticFilesStorage") +class EditBookingDatesViewTest(TestCase): + def setUp(self): + self.room_type = Room_type.objects.create( + name="Single", + max_guests=1, + price=20 + ) + + self.room_1 = Room.objects.create(name="Room 1.1", room_type=self.room_type) + self.room_2 = Room.objects.create(name="Room 1.2", room_type=self.room_type) + + self.customer = Customer.objects.create( + name="John Doe", + email="john@example.com", + phone="123456789" + ) + + self.booking = Booking.objects.create( + room=self.room_1, + customer=self.customer, + checkin=date(2022, 1, 10), + checkout=date(2022, 1, 12), + guests=1, + total=40, + code="ABC123", + state="NEW" + ) + + def test_booking_dates_are_updated_when_room_is_available(self): + response = self.client.post( + reverse("edit_booking_dates", kwargs={"pk": self.booking.id}), + { + "checkin": "2022-01-15", + "checkout": "2022-01-18", + } + ) + + self.assertEqual(response.status_code, 302) + + self.booking.refresh_from_db() + self.assertEqual(self.booking.checkin, date(2022, 1, 15)) + self.assertEqual(self.booking.checkout, date(2022, 1, 18)) + + def test_error_is_shown_when_dates_overlap_with_another_booking(self): + Booking.objects.create( + room=self.room_1, + customer=self.customer, + checkin=date(2022, 1, 14), + checkout=date(2022, 1, 16), + guests=1, + total=40, + code="XYZ789", + state="NEW" + ) + + response = self.client.post( + reverse("edit_booking_dates", kwargs={"pk": self.booking.id}), + { + "checkin": "2022-01-15", + "checkout": "2022-01-17", + } + ) + + self.assertEqual(response.status_code, 200) + self.assertContains(response, "No hay disponibilidad para las fechas seleccionadas") + + self.booking.refresh_from_db() + self.assertEqual(self.booking.checkin, date(2022, 1, 10)) + self.assertEqual(self.booking.checkout, date(2022, 1, 12)) + + def test_error_is_shown_when_checkout_is_not_later_than_checkin(self): + response = self.client.post( + reverse("edit_booking_dates", kwargs={"pk": self.booking.id}), + { + "checkin": "2022-01-15", + "checkout": "2022-01-15", + } + ) + + self.assertEqual(response.status_code, 200) + self.assertContains( + response, + "La fecha de salida debe ser posterior a la fecha de entrada." + ) + + self.booking.refresh_from_db() + self.assertEqual(self.booking.checkin, date(2022, 1, 10)) + self.assertEqual(self.booking.checkout, date(2022, 1, 12)) \ No newline at end of file diff --git a/pms/urls.py b/pms/urls.py index c18714abf..a9e701dc0 100644 --- a/pms/urls.py +++ b/pms/urls.py @@ -8,6 +8,7 @@ path("search/booking/", views.BookingSearchView.as_view(), name="booking_search"), path("booking//", views.BookingView.as_view(), name="booking"), path("booking//edit", views.EditBookingView.as_view(), name="edit_booking"), + path("booking//edit-dates/", views.EditBookingDatesView.as_view(), name="edit_booking_dates"), path("booking//delete", views.DeleteBookingView.as_view(), name="delete_booking"), path("rooms/", views.RoomsView.as_view(), name="rooms"), path("room//", views.RoomDetailsView.as_view(), name="room_details"), diff --git a/pms/views.py b/pms/views.py index f38563933..7e9cccdda 100644 --- a/pms/views.py +++ b/pms/views.py @@ -173,6 +173,45 @@ def post(self, request, pk): customer_form.save() return redirect("/") +class EditBookingDatesView(View): + # renders the booking dates edition form + def get(self, request, pk): + booking = Booking.objects.get(id=pk) + form = BookingDatesForm(instance=booking) + + context = { + "booking": booking, + "form": form, + } + return render(request, "edit_booking_dates.html", context) + + # updates the booking dates after validating room availability + def post(self, request, pk): + booking = Booking.objects.get(id=pk) + form = BookingDatesForm(request.POST, instance=booking) + + if form.is_valid(): + checkin = form.cleaned_data["checkin"] + checkout = form.cleaned_data["checkout"] + + conflicting_bookings = Booking.objects.filter( + room=booking.room, + checkin__lt=checkout, + checkout__gt=checkin, + state="NEW" + ).exclude(id=booking.id) + + if conflicting_bookings.exists(): + form.add_error(None, "No hay disponibilidad para las fechas seleccionadas") + else: + form.save() + return redirect("/") + + context = { + "booking": booking, + "form": form, + } + return render(request, "edit_booking_dates.html", context) class DashboardView(View): def get(self, request): From 6254140633f9ca632d4547716bd6332b006a650c Mon Sep 17 00:00:00 2001 From: wild_fly Date: Sat, 11 Apr 2026 21:45:16 +0200 Subject: [PATCH 2/3] change code comment's format to match prevous commits --- pms/forms.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pms/forms.py b/pms/forms.py index e211ccdaa..4e8bbeaf1 100644 --- a/pms/forms.py +++ b/pms/forms.py @@ -62,16 +62,17 @@ class Meta: model = Booking fields = ["checkin", "checkout"] - # Receives the check-in and check-out dates for a booking. - # Checks that the selected date range is valid. - # Returns cleaned data when the form is valid, or errors if validation fails. - + ''' + Receives the check-in and check-out dates for a booking. + Checks that the selected date range is valid. + Returns cleaned data when the form is valid, or errors if validation fails. + ''' def clean(self): cleaned_data = super().clean() - checkin = cleaned_data.get("checkin") - checkout = cleaned_data.get("checkout") + checkin = cleaned_data.get('checkin') + checkout = cleaned_data.get('checkout') if checkin and checkout and checkout <= checkin: - raise forms.ValidationError("La fecha de salida debe ser posterior a la fecha de entrada.") + raise forms.ValidationError('La fecha de salida debe ser posterior a la fecha de entrada.') return cleaned_data \ No newline at end of file From b4f1f57907f6a8cc30b82cb33ee0c2a2c48f728d Mon Sep 17 00:00:00 2001 From: wild_fly Date: Mon, 13 Apr 2026 15:33:20 +0200 Subject: [PATCH 3/3] change code comment's format to match prevous commits --- pms/forms.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pms/forms.py b/pms/forms.py index 4e8bbeaf1..688356790 100644 --- a/pms/forms.py +++ b/pms/forms.py @@ -63,9 +63,9 @@ class Meta: fields = ["checkin", "checkout"] ''' - Receives the check-in and check-out dates for a booking. - Checks that the selected date range is valid. - Returns cleaned data when the form is valid, or errors if validation fails. + Receives the check-in and check-out dates for a booking. + Checks that the selected date range is valid. + Returns cleaned data when the form is valid, or errors if validation fails. ''' def clean(self): cleaned_data = super().clean()