[rentals] Allow reserving lockers for internal makerspace use
This commit is contained in:
parent
3225f2ff87
commit
87df8d460c
@ -10,4 +10,5 @@ class LockerInfoForm(forms.ModelForm):
|
|||||||
"blind_code",
|
"blind_code",
|
||||||
"bitting_code",
|
"bitting_code",
|
||||||
"renter",
|
"renter",
|
||||||
|
"reserved",
|
||||||
]
|
]
|
||||||
|
30
rentals/migrations/0003_lockerinfo_reserved_and_more.py
Normal file
30
rentals/migrations/0003_lockerinfo_reserved_and_more.py
Normal file
@ -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",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@ -1,7 +1,8 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models, transaction
|
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
|
from django.db.models.functions import Chr, Ord, Concat
|
||||||
|
|
||||||
|
|
||||||
@ -85,15 +86,31 @@ class LockerInfo(models.Model):
|
|||||||
renter = models.ForeignKey(
|
renter = models.ForeignKey(
|
||||||
Member, on_delete=models.CASCADE, null=True, blank=True, db_constraint=False
|
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)
|
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:
|
class Meta:
|
||||||
constraints = [
|
constraints = [
|
||||||
models.UniqueConstraint(
|
models.UniqueConstraint(
|
||||||
fields=["locker_unit", "column", "row"], name="unique_locker_info"
|
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
|
@property
|
||||||
def letter(self) -> str:
|
def letter(self) -> str:
|
||||||
return self.locker_unit.letter_for_column(self.column)
|
return self.locker_unit.letter_for_column(self.column)
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: lightgray;
|
color: lightgray;
|
||||||
}
|
}
|
||||||
.locker .door:not([data-rented="false"]) {
|
.locker .door[data-available="False"] {
|
||||||
background-color: hsl(24, 15%, 25%);;
|
background-color: hsl(24, 15%, 25%);;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -60,7 +60,7 @@
|
|||||||
data-bs-auto-close="outside"
|
data-bs-auto-close="outside"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
data-rented="{{ locker_form.instance.renter|yesno:"true,false" }}">
|
data-available="{{ locker_form.instance.available }}">
|
||||||
<h3 class="locker-address mb-0">{{ locker_form.instance.address }}</h3>
|
<h3 class="locker-address mb-0">{{ locker_form.instance.address }}</h3>
|
||||||
<div class="locker-status text-wrap">
|
<div class="locker-status text-wrap">
|
||||||
{% if locker_form.instance.renter is not None %}
|
{% if locker_form.instance.renter is not None %}
|
||||||
@ -69,6 +69,8 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
Occupied
|
Occupied
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% elif locker_form.instance.reserved %}
|
||||||
|
Reserved
|
||||||
{% else %}
|
{% else %}
|
||||||
Empty
|
Empty
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -83,12 +85,19 @@
|
|||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<fieldset {{ perms.rentals.change_lockerinfo|yesno:",disabled" }}>
|
<fieldset {{ perms.rentals.change_lockerinfo|yesno:",disabled" }}>
|
||||||
{% for field in locker_form.visible_fields %}
|
{% for field in locker_form.visible_fields %}
|
||||||
<div class="form-floating mb-3">
|
<div class="mb-3">
|
||||||
|
{% if field.widget_type == 'checkbox' %}
|
||||||
|
{% render_field field class+="btn-check" %}
|
||||||
|
{{ field|add_label_class:"btn btn-outline-primary" }}
|
||||||
|
{% else %}
|
||||||
|
<div class="form-floating">
|
||||||
{% render_field field class+="form-control" %}
|
{% render_field field class+="form-control" %}
|
||||||
{{ field.label_tag }}
|
{{ field.label_tag }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{{ field.errors }}
|
{{ field.errors }}
|
||||||
{% if field.help_text %}
|
{% if field.help_text %}
|
||||||
<p class="form-text text-nowrap">
|
<p class="form-text">
|
||||||
{{ field.help_text|safe }}
|
{{ field.help_text|safe }}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
Loading…
Reference in New Issue
Block a user