add detailview, mail sending and new db values

This commit is contained in:
2025-03-05 21:11:03 +01:00
parent dc263ee28c
commit 77d18cf58c
10 changed files with 182 additions and 21 deletions

View File

@@ -14,9 +14,14 @@ class RentalAdmin(admin.ModelAdmin):
"firstname",
"surname",
"status",
"total_disposit",
"date_start",
"date_end",
]
ordering = ["-id"]
readonly_fields = ["total_disposit"]
fieldsets = (
(
"Persönliche Daten",
@@ -35,6 +40,7 @@ class RentalAdmin(admin.ModelAdmin):
("date_start", "date_end"),
"reason",
"rentalitems",
"total_disposit",
),
},
),
@@ -63,12 +69,22 @@ class RentalAdmin(admin.ModelAdmin):
obj.author = request.user
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)
class RentalItemAdmin(admin.ModelAdmin):
form = RentalItemAdminForm
model = RentalItem
ordering = ["name"]
def add_view(self, request, form_url="", extra_context=None):
extra_context = extra_context or {}
extra_context["help_text"] = "Fette Schriften sind Pflichtfelder."

View File

@@ -12,14 +12,7 @@ class RentalCreateForm(forms.ModelForm):
# Conformation
conformation = forms.BooleanField(
required=True,
label=(
"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. "
),
label=("Ich habe die Verleihregeln gelesen und akzeptiere sie."),
initial=False,
)

39
fet2020/rental/mails.py Normal file
View 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)

View File

@@ -1,6 +1,7 @@
from django.db import models
from django.forms import ValidationError
from .mails import send_mail_approved, send_mail_rejected
from .validators import PhoneNumberValidator
@@ -10,6 +11,16 @@ class RentalItem(models.Model):
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:
verbose_name = "Verleihgegenstand"
verbose_name_plural = "Verleihgegenstände"
@@ -63,6 +74,24 @@ class Rental(models.Model):
def __str__(self):
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):
if not self.date_end:
self.date_end = self.date_start

Binary file not shown.

View File

@@ -1,7 +1,7 @@
from django.urls import path
from . import apps
from .views import RentalCreateDoneView, RentalCreateView, RentalListView
from .views import RentalCreateDoneView, RentalCreateView, RentalItemDetailView, RentalListView
app_name = apps.RentalConfig.name
@@ -13,4 +13,5 @@ urlpatterns = [
RentalCreateDoneView.as_view(),
name="rental_create_done",
),
path("rentalitems/<int:pk>/", RentalItemDetailView.as_view(), name="rentalitem_detail"),
]

View File

@@ -4,6 +4,7 @@ import datetime
from django.db.models import Q
from django.urls import reverse
from django.views.generic import ListView, TemplateView
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView
from .forms import RentalCreateForm
@@ -150,3 +151,22 @@ class RentalCreateView(CreateView):
class RentalCreateDoneView(TemplateView):
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"

View File

@@ -1,5 +1,7 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}Verleih Anfrage{% endblock %}
{% block content %}
@@ -13,7 +15,7 @@
<section>
<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="sm:col-span-3">
@@ -39,7 +41,7 @@
<section>
<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">
{% if form.rentalitems.errors %}
@@ -59,13 +61,16 @@
name="{{ form.rentalitems.html_name }}"
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"
>
<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>
{% for item in rentalitems_addinfo %}
{% if item.name == elem.choice_label and item.description %}
<p class="text-xs text-gray-700 dark:text-gray-200">{{ item.description }}</p>
{% if item.name == elem.choice_label and item.induction %}
<p class="text-xs text-gray-700 dark:text-gray-200">Einschulung erforderlich!</p>
{% endif %}
{% endfor %}
</div>
@@ -87,7 +92,7 @@
<section>
<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="col-span-full">
@@ -98,9 +103,13 @@
<section>
<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 %}
</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>
</section>

View File

@@ -4,10 +4,10 @@
{% block content %}
<!-- Main Content -->
<main class="container mx-auto w-full px-4 my-8 flex-1">
<section class="w-full text-center">
<p class="mt-6 text-gray-900 dark:text-gray-100">
Die Verleihanfrage mit der Nummer #{{ pk }} wurde erfolgreich eingereicht.
<main class="container mx-auto w-full px-4 my-8 flex-1 max-w-2xl">
<section class="block w-full">
<p class="mt-6 text-gray-900 dark:text-gray-100 hyphens-auto" lang="de">
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>
<div class="mt-10 flex items-center justify-center">
<a href="{% url 'home' %}" type="submit" class="block btn btn-primary">Zur Startseite</a>

View 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 %}