add detailview, mail sending and new db values
This commit is contained in:
@@ -14,9 +14,14 @@ class RentalAdmin(admin.ModelAdmin):
|
|||||||
"firstname",
|
"firstname",
|
||||||
"surname",
|
"surname",
|
||||||
"status",
|
"status",
|
||||||
|
"total_disposit",
|
||||||
"date_start",
|
"date_start",
|
||||||
"date_end",
|
"date_end",
|
||||||
]
|
]
|
||||||
|
ordering = ["-id"]
|
||||||
|
|
||||||
|
readonly_fields = ["total_disposit"]
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
"Persönliche Daten",
|
"Persönliche Daten",
|
||||||
@@ -35,6 +40,7 @@ class RentalAdmin(admin.ModelAdmin):
|
|||||||
("date_start", "date_end"),
|
("date_start", "date_end"),
|
||||||
"reason",
|
"reason",
|
||||||
"rentalitems",
|
"rentalitems",
|
||||||
|
"total_disposit",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -63,12 +69,22 @@ class RentalAdmin(admin.ModelAdmin):
|
|||||||
obj.author = request.user
|
obj.author = request.user
|
||||||
super().save_model(request, obj, form, change)
|
super().save_model(request, obj, form, change)
|
||||||
|
|
||||||
|
@admin.display(description="Kaution (EUR)")
|
||||||
|
def total_disposit(self, obj):
|
||||||
|
total_disposit = 0
|
||||||
|
for elem in obj.rentalitems.all():
|
||||||
|
total_disposit += elem.deposit
|
||||||
|
|
||||||
|
return f"{total_disposit}"
|
||||||
|
|
||||||
|
|
||||||
@admin.register(RentalItem)
|
@admin.register(RentalItem)
|
||||||
class RentalItemAdmin(admin.ModelAdmin):
|
class RentalItemAdmin(admin.ModelAdmin):
|
||||||
form = RentalItemAdminForm
|
form = RentalItemAdminForm
|
||||||
model = RentalItem
|
model = RentalItem
|
||||||
|
|
||||||
|
ordering = ["name"]
|
||||||
|
|
||||||
def add_view(self, request, form_url="", extra_context=None):
|
def add_view(self, request, form_url="", extra_context=None):
|
||||||
extra_context = extra_context or {}
|
extra_context = extra_context or {}
|
||||||
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
|
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."
|
||||||
|
|||||||
@@ -12,14 +12,7 @@ class RentalCreateForm(forms.ModelForm):
|
|||||||
# Conformation
|
# Conformation
|
||||||
conformation = forms.BooleanField(
|
conformation = forms.BooleanField(
|
||||||
required=True,
|
required=True,
|
||||||
label=(
|
label=("Ich habe die Verleihregeln gelesen und akzeptiere sie."),
|
||||||
"1. Fachschaft Elektrotechnik (FET) hat stets Vorrecht. 2. Bereits bestätigte Objekte "
|
|
||||||
"können storniert werden, falls FET sie selber braucht. 3. Sachen dürfen nur über das "
|
|
||||||
"Verleihsystem verborgt werden. 4. Objekte müssen pünktlich und gereinigt retouniert "
|
|
||||||
"werden. 5. Verleihpersonen entscheiden über Kaution (lediglich für Pünktlichkeit und "
|
|
||||||
"Reinheit) 6. Geht was kaputt, muss dies umgehend mitgeteilt werden und finanziell "
|
|
||||||
"dafür aufgekommen werden. "
|
|
||||||
),
|
|
||||||
initial=False,
|
initial=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
39
fet2020/rental/mails.py
Normal file
39
fet2020/rental/mails.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from django.core.mail import send_mail
|
||||||
|
|
||||||
|
RENTAL_EMAIL = "patrick@fet.at"
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def send_mail_approved(obj):
|
||||||
|
subject = f"Verleih #{obj.id}: {obj.get_status_display()}"
|
||||||
|
|
||||||
|
total_deposit = 0
|
||||||
|
for rentalitem in obj.rentalitems.all():
|
||||||
|
total_deposit += rentalitem.deposit
|
||||||
|
|
||||||
|
message = (
|
||||||
|
f"Deine Verleihanfrage mit der Nummer #{obj.id} wurde erfolgreich genehmigt. Die "
|
||||||
|
f"Gegenstände können am {obj.date_start.strftime('%d.%m.%Y')} während der Beratungszeit "
|
||||||
|
"(Montag - Donnerstag: 09:00 - 14:00, Freitag: 09:00 - 12:00) abgeholt werden. Bitte bring "
|
||||||
|
f"den Gesamtpfand in Höhe von {total_deposit} € in bar mit.\nLiebe Grüße,\ndas Verleih-Team"
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
send_mail(subject, message, RENTAL_EMAIL, [f"{obj.email}", RENTAL_EMAIL])
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error("Failed to send approval email for rental #%s. Error: %s", obj.id, exc)
|
||||||
|
|
||||||
|
|
||||||
|
def send_mail_rejected(obj):
|
||||||
|
subject = f"Verleih #{obj.id}: {obj.get_status_display()}"
|
||||||
|
message = (
|
||||||
|
f"Deine Verleihanfrage mit der Nummer #{obj.id} wurde abgelehnt.\nLiebe Grüße,\ndas "
|
||||||
|
"Verleih-Team"
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
send_mail(subject, message, RENTAL_EMAIL, [f"{obj.email}", RENTAL_EMAIL])
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error("Failed to send rejection email for rental #%s. Error: %s", obj.id, exc)
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.forms import ValidationError
|
from django.forms import ValidationError
|
||||||
|
|
||||||
|
from .mails import send_mail_approved, send_mail_rejected
|
||||||
from .validators import PhoneNumberValidator
|
from .validators import PhoneNumberValidator
|
||||||
|
|
||||||
|
|
||||||
@@ -10,6 +11,16 @@ class RentalItem(models.Model):
|
|||||||
verbose_name="Beschreibung", max_length=128, blank=True, default=""
|
verbose_name="Beschreibung", max_length=128, blank=True, default=""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
deposit = models.DecimalField(
|
||||||
|
verbose_name="Kaution (EUR)", max_digits=6, decimal_places=2, default=0
|
||||||
|
)
|
||||||
|
|
||||||
|
image = models.ImageField(verbose_name="Bild", upload_to="rentalitems", blank=True, null=True)
|
||||||
|
|
||||||
|
induction = models.BooleanField(verbose_name="Einschulung notwendig", default=False)
|
||||||
|
|
||||||
|
location = models.CharField(verbose_name="Standort", max_length=128, blank=True, default="")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Verleihgegenstand"
|
verbose_name = "Verleihgegenstand"
|
||||||
verbose_name_plural = "Verleihgegenstände"
|
verbose_name_plural = "Verleihgegenstände"
|
||||||
@@ -63,6 +74,24 @@ class Rental(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Verleih #{self.id}: {self.firstname} {self.surname}"
|
return f"Verleih #{self.id}: {self.firstname} {self.surname}"
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
pre_obj = Rental.objects.filter(pk=self.pk).first()
|
||||||
|
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
if (
|
||||||
|
pre_obj
|
||||||
|
and pre_obj.status != self.Status.APPROVED
|
||||||
|
and self.status == self.Status.APPROVED
|
||||||
|
):
|
||||||
|
send_mail_approved(self)
|
||||||
|
elif (
|
||||||
|
pre_obj
|
||||||
|
and pre_obj.status != self.Status.REJECTED
|
||||||
|
and self.status == self.Status.REJECTED
|
||||||
|
):
|
||||||
|
send_mail_rejected(self)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if not self.date_end:
|
if not self.date_end:
|
||||||
self.date_end = self.date_start
|
self.date_end = self.date_start
|
||||||
|
|||||||
BIN
fet2020/rental/static/rental/Verleihregeln.pdf
Normal file
BIN
fet2020/rental/static/rental/Verleihregeln.pdf
Normal file
Binary file not shown.
@@ -1,7 +1,7 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from . import apps
|
from . import apps
|
||||||
from .views import RentalCreateDoneView, RentalCreateView, RentalListView
|
from .views import RentalCreateDoneView, RentalCreateView, RentalItemDetailView, RentalListView
|
||||||
|
|
||||||
app_name = apps.RentalConfig.name
|
app_name = apps.RentalConfig.name
|
||||||
|
|
||||||
@@ -13,4 +13,5 @@ urlpatterns = [
|
|||||||
RentalCreateDoneView.as_view(),
|
RentalCreateDoneView.as_view(),
|
||||||
name="rental_create_done",
|
name="rental_create_done",
|
||||||
),
|
),
|
||||||
|
path("rentalitems/<int:pk>/", RentalItemDetailView.as_view(), name="rentalitem_detail"),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import datetime
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.views.generic import ListView, TemplateView
|
from django.views.generic import ListView, TemplateView
|
||||||
|
from django.views.generic.detail import DetailView
|
||||||
from django.views.generic.edit import CreateView
|
from django.views.generic.edit import CreateView
|
||||||
|
|
||||||
from .forms import RentalCreateForm
|
from .forms import RentalCreateForm
|
||||||
@@ -150,3 +151,22 @@ class RentalCreateView(CreateView):
|
|||||||
|
|
||||||
class RentalCreateDoneView(TemplateView):
|
class RentalCreateDoneView(TemplateView):
|
||||||
template_name = "rental/create_done.html"
|
template_name = "rental/create_done.html"
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
obj = Rental.objects.get(pk=self.kwargs["pk"])
|
||||||
|
|
||||||
|
total_deposit = 0
|
||||||
|
for elem in obj.rentalitems.all():
|
||||||
|
total_deposit += elem.deposit
|
||||||
|
|
||||||
|
context["object"] = obj
|
||||||
|
context["total_deposit"] = total_deposit
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class RentalItemDetailView(DetailView):
|
||||||
|
model = RentalItem
|
||||||
|
template_name = "rental/rentalitem_detail.html"
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
{% block title %}Verleih Anfrage{% endblock %}
|
{% block title %}Verleih Anfrage{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@@ -13,7 +15,7 @@
|
|||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Persönliche Daten</h2>
|
<h2>Persönliche Daten</h2>
|
||||||
<small>Bitte geben Sie Ihre persönlichen Daten ein.</small>
|
<small>Bitte gib deine persönlichen Daten ein.</small>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-x-6 gap-y-6 sm:grid-cols-6">
|
<div class="grid grid-cols-1 gap-x-6 gap-y-6 sm:grid-cols-6">
|
||||||
<div class="sm:col-span-3">
|
<div class="sm:col-span-3">
|
||||||
@@ -39,7 +41,7 @@
|
|||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Verleihgegenstände</h2>
|
<h2>Verleihgegenstände</h2>
|
||||||
<small>Wählen Sie die gewünschten Verleihgegenstände aus.</small>
|
<small>Wähl deine gewünschten Verleihgegenstände aus.</small>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-x-6 gap-y-6 sm:grid-cols-6 pb-6 col-span-full">
|
<div class="grid grid-cols-1 gap-x-6 gap-y-6 sm:grid-cols-6 pb-6 col-span-full">
|
||||||
{% if form.rentalitems.errors %}
|
{% if form.rentalitems.errors %}
|
||||||
@@ -59,13 +61,16 @@
|
|||||||
name="{{ form.rentalitems.html_name }}"
|
name="{{ form.rentalitems.html_name }}"
|
||||||
value="{{ elem.data.value }}"
|
value="{{ elem.data.value }}"
|
||||||
class="rounded border-gray-300 dark:border-none text-proprietary shadow-sm focus:border-blue-300 focus:ring focus:ring-offset-0 focus:ring-blue-200 dark:focus:ring-sky-700 focus:ring-opacity-50"
|
class="rounded border-gray-300 dark:border-none text-proprietary shadow-sm focus:border-blue-300 focus:ring focus:ring-offset-0 focus:ring-blue-200 dark:focus:ring-sky-700 focus:ring-opacity-50"
|
||||||
>
|
>
|
||||||
<span>{{ elem.choice_label }}</span>
|
<a
|
||||||
|
href="{% url 'rental:rentalitem_detail' elem.data.value %}"
|
||||||
|
class="text-gray-700 dark:text-gray-200"
|
||||||
|
>{{ elem.choice_label }}</a>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
{% for item in rentalitems_addinfo %}
|
{% for item in rentalitems_addinfo %}
|
||||||
{% if item.name == elem.choice_label and item.description %}
|
{% if item.name == elem.choice_label and item.induction %}
|
||||||
<p class="text-xs text-gray-700 dark:text-gray-200">{{ item.description }}</p>
|
<p class="text-xs text-gray-700 dark:text-gray-200">Einschulung erforderlich!</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
@@ -87,7 +92,7 @@
|
|||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Zusätzliche Informationen</h2>
|
<h2>Zusätzliche Informationen</h2>
|
||||||
<small>Hier können Sie zusätzliche Informationen, Anliegen und Sonstiges angeben.</small>
|
<small>Hier kannst du zusätzliche Informationen, Anliegen und Sonstiges angeben.</small>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-x-6 gap-y-6 sm:grid-cols-6">
|
<div class="grid grid-cols-1 gap-x-6 gap-y-6 sm:grid-cols-6">
|
||||||
<div class="col-span-full">
|
<div class="col-span-full">
|
||||||
@@ -98,9 +103,13 @@
|
|||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div class="grid grid-cols-1 gap-x-6 gap-y-6 sm:grid-cols-6">
|
<div class="grid grid-cols-1 gap-x-6 gap-y-6 sm:grid-cols-6">
|
||||||
<div class="col-span-full">
|
<div class="col-span-full text-gray-700 dark:text-gray-200">
|
||||||
{% include "baseform/checkbox.html" with field=form.conformation %}
|
{% include "baseform/checkbox.html" with field=form.conformation %}
|
||||||
</div>
|
</div>
|
||||||
|
<a href="{% static 'rental/verleihregeln.pdf' %}" target='_blank' class="inline-flex items-center px-2 py-1">
|
||||||
|
<i class="fa-solid fa-file-pdf fa-fw text-red-800 dark:text-red-500 md:text-inherit group-hover:text-red-800 dark:group-hover:text-red-500"></i>
|
||||||
|
<span class="ml-2 sm:ml-1 text-gray-700 dark:text-gray-200">Verleihregeln</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<main class="container mx-auto w-full px-4 my-8 flex-1">
|
<main class="container mx-auto w-full px-4 my-8 flex-1 max-w-2xl">
|
||||||
<section class="w-full text-center">
|
<section class="block w-full">
|
||||||
<p class="mt-6 text-gray-900 dark:text-gray-100">
|
<p class="mt-6 text-gray-900 dark:text-gray-100 hyphens-auto" lang="de">
|
||||||
Die Verleihanfrage mit der Nummer #{{ pk }} wurde erfolgreich eingereicht.
|
Deine Verleihanfrage mit der Nummer #{{ pk }} wurde erfolgreich eingereicht. Die Gegenstände können am {{ object.date_start }} während der Beratungszeit (Montag - Donnerstag: 09:00 - 14:00, Freitag: 09:00 - 12:00) abgeholt werden. Bitte bring den Gesamtpfand in Höhe von {{ total_deposit }} € in bar mit.
|
||||||
</p>
|
</p>
|
||||||
<div class="mt-10 flex items-center justify-center">
|
<div class="mt-10 flex items-center justify-center">
|
||||||
<a href="{% url 'home' %}" type="submit" class="block btn btn-primary">Zur Startseite</a>
|
<a href="{% url 'home' %}" type="submit" class="block btn btn-primary">Zur Startseite</a>
|
||||||
|
|||||||
54
fet2020/templates/rental/rentalitem_detail.html
Normal file
54
fet2020/templates/rental/rentalitem_detail.html
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block title %}Verleihgegenstand {{ object.name }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="container mx-auto w-full px-4 my-8 flex-1 max-w-2xl">
|
||||||
|
<h1 class="page-title">Verleihgegenstand {{ object.name }}</h1>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="grid grid-cols-1 gap-x-6 gap-y-6 sm:grid-cols-6">
|
||||||
|
{% if object.description %}
|
||||||
|
<div class="col-span-full">
|
||||||
|
<label>
|
||||||
|
<h2 class="text-xl font-black text-gray-700 dark:text-gray-200">Beschreibung</h2>
|
||||||
|
<p class="text-gray-700 dark:text-gray-200">{{ object.description|safe }}</p>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if object.location %}
|
||||||
|
<div class="col-span-full">
|
||||||
|
<label>
|
||||||
|
<h2 class="text-xl font-black text-gray-700 dark:text-gray-200">Standort</h2>
|
||||||
|
<p class="text-gray-700 dark:text-gray-200">{{ object.location }}</p>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if object.image %}
|
||||||
|
<div class="col-span-full">
|
||||||
|
<label>
|
||||||
|
<h2 class="text-xl font-black text-gray-700 dark:text-gray-200">Bild</h2>
|
||||||
|
<img src="{{ object.image.url }}" alt="{{ object.name }}" class="w-full h-auto">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if object.deposit %}
|
||||||
|
<div class="col-span-full">
|
||||||
|
<label>
|
||||||
|
<h2 class="text-xl font-black text-gray-700 dark:text-gray-200">Kaution</h2>
|
||||||
|
<p class="text-gray-700 dark:text-gray-200">{{ object.deposit }} €</p>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if object.induction %}
|
||||||
|
<div class="col-span-full">
|
||||||
|
<label>
|
||||||
|
<h2 class="text-xl font-black text-gray-700 dark:text-gray-200">Einschulung erforderlich.</h2>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
{% endblock content %}
|
||||||
Reference in New Issue
Block a user