diff --git a/rentals/forms.py b/rentals/forms.py index 911fb06..8497dbd 100644 --- a/rentals/forms.py +++ b/rentals/forms.py @@ -10,4 +10,5 @@ class LockerInfoForm(forms.ModelForm): "blind_code", "bitting_code", "renter", + "reserved", ] diff --git a/rentals/migrations/0003_lockerinfo_reserved_and_more.py b/rentals/migrations/0003_lockerinfo_reserved_and_more.py new file mode 100644 index 0000000..4467cee --- /dev/null +++ b/rentals/migrations/0003_lockerinfo_reserved_and_more.py @@ -0,0 +1,30 @@ +# Generated by Django 4.0.2 on 2022-03-16 01:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("rentals", "0002_lockerinfo_notes"), + ] + + operations = [ + migrations.AddField( + model_name="lockerinfo", + name="reserved", + field=models.BooleanField( + default=False, + help_text="Locker is reserved for MakerSpace use, and cannot be rented.", + ), + ), + migrations.AddConstraint( + model_name="lockerinfo", + constraint=models.CheckConstraint( + check=models.Q( + ("reserved", False), ("renter__isnull", True), _connector="OR" + ), + name="locker_not_reserved_and_rented", + ), + ), + ] diff --git a/rentals/models.py b/rentals/models.py index 55b1a4d..09c0cf3 100644 --- a/rentals/models.py +++ b/rentals/models.py @@ -1,7 +1,8 @@ from django.contrib import admin from django.core import validators +from django.core.exceptions import ValidationError from django.db import models, transaction -from django.db.models import F +from django.db.models import F, Q from django.db.models.functions import Chr, Ord, Concat @@ -85,15 +86,31 @@ class LockerInfo(models.Model): renter = models.ForeignKey( Member, on_delete=models.CASCADE, null=True, blank=True, db_constraint=False ) + reserved = models.BooleanField( + default=False, + help_text="Locker is reserved for MakerSpace use, and cannot be rented.", + ) notes = models.TextField(blank=True) + def clean(self): + if self.reserved and self.renter is not None: + raise ValidationError("Locker cannot both be reserved and rented!") + class Meta: constraints = [ models.UniqueConstraint( fields=["locker_unit", "column", "row"], name="unique_locker_info" - ) + ), + models.CheckConstraint( + check=Q(reserved=False) | Q(renter__isnull=True), + name="locker_not_reserved_and_rented", + ), ] + @property + def available(self) -> bool: + return self.renter is None and not self.reserved + @property def letter(self) -> str: return self.locker_unit.letter_for_column(self.column) diff --git a/rentals/templates/rentals/lockers.dj.html b/rentals/templates/rentals/lockers.dj.html index b869700..efd50af 100644 --- a/rentals/templates/rentals/lockers.dj.html +++ b/rentals/templates/rentals/lockers.dj.html @@ -39,7 +39,7 @@ justify-content: center; color: lightgray; } - .locker .door:not([data-rented="false"]) { + .locker .door[data-available="False"] { background-color: hsl(24, 15%, 25%);; } @@ -60,7 +60,7 @@ data-bs-auto-close="outside" aria-haspopup="true" aria-expanded="false" - data-rented="{{ locker_form.instance.renter|yesno:"true,false" }}"> + data-available="{{ locker_form.instance.available }}">