Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pms/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ class Meta:
}


class EditBookingDatesForm(forms.Form):
checkin = forms.DateField(widget=forms.DateInput(attrs={'type': 'date'}), label="Fecha de entrada")
checkout = forms.DateField(widget=forms.DateInput(attrs={'type': 'date'}), label="Fecha de salida")


class BookingFormExcluded(ModelForm):
class Meta:
model = Booking
Expand Down
26 changes: 26 additions & 0 deletions pms/templates/edit_booking_dates.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{% extends "main.html"%}

{% block content %}
<h1>Editar fechas de reserva</h1>
<h5>Reserva: {{booking.code}} - {{booking.room.name}}</h5>
<div class="card card-body">
<form method="post" action="">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
<p>{{ error }}</p>
{% endfor %}
</div>
{% endif %}
{% for field in form %}
<div class="row mb-2">
<div class="col-md-2">{{field.label_tag}}</div>
<div class="col-md-auto">{{field}}</div>
</div>
{% endfor %}
<a class="btn btn-outline-primary" href="{% url 'home' %}">Volver</a>
<button class="btn btn-primary" type="submit">Guardar</button>
</form>
</div>
{% endblock content%}
4 changes: 3 additions & 1 deletion pms/templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ <h3>Reservas Realizadas</h3>
<a href="{% url 'edit_booking' pk=booking.id%} " >Editar datos de contacto</a>
</div>
<div class="col">

{% if booking.state != "DEL" %}
<a href="{% url 'edit_booking_dates' pk=booking.id%}">Editar fechas</a>
{% endif %}
</div>
<div class="col">

Expand Down
90 changes: 88 additions & 2 deletions pms/tests.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,89 @@
from django.test import TestCase
from datetime import date

# Create your tests here.
from django.test import TestCase, Client
from django.urls import reverse

from .models import Room, Room_type, Booking, Customer


class EditBookingDatesViewTest(TestCase):
def setUp(self):
self.client = Client()
self.room_type = Room_type.objects.create(name="Individual", price=20, max_guests=1)
self.room = Room.objects.create(name="Room 1", room_type=self.room_type, description="")
self.customer = Customer.objects.create(name="Test", email="test@test.com", phone="123")
self.booking = Booking.objects.create(
checkin=date(2025, 6, 1), checkout=date(2025, 6, 5),
room=self.room, customer=self.customer,
guests=1, total=80, code="AAAA1111", state="NEW"
)

def test_get_edit_dates_form(self):
response = self.client.get(reverse("edit_booking_dates", args=[self.booking.id]))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Editar fechas de reserva")

def test_edit_dates_success(self):
response = self.client.post(
reverse("edit_booking_dates", args=[self.booking.id]),
{"checkin": "2025-07-01", "checkout": "2025-07-03"}
)
self.assertRedirects(response, "/")
self.booking.refresh_from_db()
self.assertEqual(self.booking.checkin, date(2025, 7, 1))
self.assertEqual(self.booking.checkout, date(2025, 7, 3))
# total recalculated: 2 days * 20 = 40
self.assertEqual(self.booking.total, 40)

def test_edit_dates_conflict_shows_error(self):
# Another booking on the same room overlapping the new dates
Booking.objects.create(
checkin=date(2025, 7, 1), checkout=date(2025, 7, 5),
room=self.room, customer=self.customer,
guests=1, total=80, code="BBBB2222", state="NEW"
)
response = self.client.post(
reverse("edit_booking_dates", args=[self.booking.id]),
{"checkin": "2025-07-02", "checkout": "2025-07-04"}
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "No hay disponibilidad para las fechas seleccionadas")
# Booking unchanged
self.booking.refresh_from_db()
self.assertEqual(self.booking.checkin, date(2025, 6, 1))

def test_edit_dates_no_conflict_with_itself(self):
# Shifting the same booking's dates slightly should not conflict with itself
response = self.client.post(
reverse("edit_booking_dates", args=[self.booking.id]),
{"checkin": "2025-06-02", "checkout": "2025-06-06"}
)
self.assertRedirects(response, "/")
self.booking.refresh_from_db()
self.assertEqual(self.booking.checkin, date(2025, 6, 2))

def test_edit_dates_ignores_deleted_bookings(self):
# A deleted booking on same room/dates should not block
Booking.objects.create(
checkin=date(2025, 7, 1), checkout=date(2025, 7, 5),
room=self.room, customer=self.customer,
guests=1, total=80, code="CCCC3333", state="DEL"
)
response = self.client.post(
reverse("edit_booking_dates", args=[self.booking.id]),
{"checkin": "2025-07-02", "checkout": "2025-07-04"}
)
self.assertRedirects(response, "/")

def test_edit_dates_allows_adjacent_bookings(self):
# Booking on same room ending on the day the new one starts (same-day checkout/checkin)
Booking.objects.create(
checkin=date(2025, 7, 1), checkout=date(2025, 7, 5),
room=self.room, customer=self.customer,
guests=1, total=80, code="DDDD4444", state="NEW"
)
response = self.client.post(
reverse("edit_booking_dates", args=[self.booking.id]),
{"checkin": "2025-07-05", "checkout": "2025-07-08"}
)
self.assertRedirects(response, "/")
1 change: 1 addition & 0 deletions pms/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
path("search/booking/", views.BookingSearchView.as_view(), name="booking_search"),
path("booking/<str:pk>/", views.BookingView.as_view(), name="booking"),
path("booking/<str:pk>/edit", views.EditBookingView.as_view(), name="edit_booking"),
path("booking/<str:pk>/edit-dates", views.EditBookingDatesView.as_view(), name="edit_booking_dates"),
path("booking/<str:pk>/delete", views.DeleteBookingView.as_view(), name="delete_booking"),
path("rooms/", views.RoomsView.as_view(), name="rooms"),
path("room/<str:pk>/", views.RoomDetailsView.as_view(), name="room_details"),
Expand Down
38 changes: 38 additions & 0 deletions pms/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from .form_dates import Ymd
from .forms import *
from .forms import EditBookingDatesForm
from .models import Room
from .reservation_code import generate

Expand Down Expand Up @@ -174,6 +175,43 @@ def post(self, request, pk):
return redirect("/")


class EditBookingDatesView(View):
def get(self, request, pk):
booking = Booking.objects.get(id=pk)
form = EditBookingDatesForm(initial={
'checkin': booking.checkin,
'checkout': booking.checkout,
})
context = {'form': form, 'booking': booking}
return render(request, "edit_booking_dates.html", context)

@method_decorator(ensure_csrf_cookie)
def post(self, request, pk):
booking = Booking.objects.get(id=pk)
form = EditBookingDatesForm(request.POST)
if form.is_valid():
checkin = form.cleaned_data['checkin']
checkout = form.cleaned_data['checkout']
# Check room availability excluding the current booking
conflicting = Booking.objects.filter(
room=booking.room,
state="NEW",
checkin__lt=checkout,
checkout__gt=checkin,
).exclude(id=booking.id).exists()
if conflicting:
form.add_error(None, "No hay disponibilidad para las fechas seleccionadas")
else:
booking.checkin = checkin
booking.checkout = checkout
total_days = (checkout - checkin).days
booking.total = total_days * booking.room.room_type.price
booking.save()
return redirect("/")
context = {'form': form, 'booking': booking}
return render(request, "edit_booking_dates.html", context)


class DashboardView(View):
def get(self, request):
from datetime import date, time, datetime
Expand Down