add Verleihformular
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin, messages
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
|
||||||
from .forms import RentalAdminForm, RentalItemAdminForm
|
from .forms import RentalAdminForm, RentalItemAdminForm
|
||||||
from .models import Rental, RentalItem
|
from .models import Rental, RentalItem
|
||||||
|
from .utils import generate_rental_pdf
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Rental)
|
@admin.register(Rental)
|
||||||
@@ -49,6 +51,7 @@ class RentalAdmin(admin.ModelAdmin):
|
|||||||
{
|
{
|
||||||
"fields": (
|
"fields": (
|
||||||
"comment",
|
"comment",
|
||||||
|
"file_field",
|
||||||
"status",
|
"status",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -63,8 +66,29 @@ class RentalAdmin(admin.ModelAdmin):
|
|||||||
def change_view(self, request, object_id, form_url="", extra_context=None):
|
def change_view(self, request, object_id, 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."
|
||||||
|
extra_context["generate_rental_pdf"] = True
|
||||||
return super().change_view(request, object_id, form_url, extra_context=extra_context)
|
return super().change_view(request, object_id, form_url, extra_context=extra_context)
|
||||||
|
|
||||||
|
def response_change(self, request, obj):
|
||||||
|
if "_generate_rental_pdf" in request.POST:
|
||||||
|
if generate_rental_pdf(obj):
|
||||||
|
self.message_user(
|
||||||
|
request,
|
||||||
|
"Neues Verleihformular wurde generiert.",
|
||||||
|
messages.SUCCESS,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.message_user(
|
||||||
|
request,
|
||||||
|
(
|
||||||
|
"Das PDF-Dokument konnte nicht generiert werden, da der Status nicht auf "
|
||||||
|
"'Verleih genehmigt' gesetzt ist."
|
||||||
|
),
|
||||||
|
messages.WARNING,
|
||||||
|
)
|
||||||
|
return HttpResponseRedirect(".")
|
||||||
|
return super().response_change(request, obj)
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
obj.author = request.user
|
obj.author = request.user
|
||||||
super().save_model(request, obj, form, change)
|
super().save_model(request, obj, form, change)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from django.core.validators import FileExtensionValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.forms import ValidationError
|
from django.forms import ValidationError
|
||||||
|
|
||||||
@@ -48,6 +49,14 @@ class Rental(models.Model):
|
|||||||
|
|
||||||
comment = models.TextField(verbose_name="Kommentar", max_length=500, blank=True, default="")
|
comment = models.TextField(verbose_name="Kommentar", max_length=500, blank=True, default="")
|
||||||
|
|
||||||
|
file_field = models.FileField(
|
||||||
|
upload_to="uploads/rental/rental/",
|
||||||
|
validators=[FileExtensionValidator(["pdf"])],
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
verbose_name="Verleihformular",
|
||||||
|
)
|
||||||
|
|
||||||
class Status(models.TextChoices):
|
class Status(models.TextChoices):
|
||||||
SUBMITTED = "S", "Eingereicht"
|
SUBMITTED = "S", "Eingereicht"
|
||||||
APPROVED = "A", "Verleih genehmigt"
|
APPROVED = "A", "Verleih genehmigt"
|
||||||
|
|||||||
BIN
fet2020/rental/static/rental/Verleihformular.pdf
Normal file
BIN
fet2020/rental/static/rental/Verleihformular.pdf
Normal file
Binary file not shown.
66
fet2020/rental/utils.py
Normal file
66
fet2020/rental/utils.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import io
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from django.core.files import File
|
||||||
|
from pypdf import PdfReader, PdfWriter
|
||||||
|
|
||||||
|
from .models import Rental
|
||||||
|
|
||||||
|
|
||||||
|
def generate_rental_pdf(rental: Rental) -> bool:
|
||||||
|
if not rental or rental.status != Rental.Status.APPROVED:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Get data for pdf
|
||||||
|
data = {}
|
||||||
|
data.update(
|
||||||
|
{
|
||||||
|
"Vorname": rental.firstname,
|
||||||
|
"Nachname": rental.surname,
|
||||||
|
"Orga": rental.organization,
|
||||||
|
"Matrikelnummer": rental.matriculation_number,
|
||||||
|
"E-Mail": rental.email,
|
||||||
|
"Telefonnummer": rental.phone,
|
||||||
|
# Change to the correct date format
|
||||||
|
"Abholdatum": str(rental.date_start.strftime("%d.%m.%Y")),
|
||||||
|
"Rückgabedatum": str(rental.date_end.strftime("%d.%m.%Y")),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
total_deposit = 0
|
||||||
|
|
||||||
|
for i, item in enumerate(rental.rentalitems.all(), start=1):
|
||||||
|
total_deposit += item.deposit
|
||||||
|
data.update(
|
||||||
|
{
|
||||||
|
f"Produkt Row{i}": item.name,
|
||||||
|
f"Menge Row{i}": "1",
|
||||||
|
f"Kaution Row{i}": item.deposit,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
data.update(
|
||||||
|
{
|
||||||
|
"Gesamtkaution": total_deposit,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Write data in pdf
|
||||||
|
pdf_path = Path(Path(__file__).parent) / "static/rental/Verleihformular.pdf"
|
||||||
|
reader = PdfReader(pdf_path)
|
||||||
|
writer = PdfWriter()
|
||||||
|
writer.append(reader)
|
||||||
|
|
||||||
|
writer.update_page_form_field_values(
|
||||||
|
writer.pages[0],
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
|
||||||
|
with io.BytesIO() as bytes_stream:
|
||||||
|
writer.write(bytes_stream)
|
||||||
|
|
||||||
|
# Save pdf in rental
|
||||||
|
rental_name = f"Verleihformular-{str(rental.pk).zfill(4)}.pdf"
|
||||||
|
rental.file_field.save(rental_name, File(bytes_stream, rental_name))
|
||||||
|
|
||||||
|
return True
|
||||||
@@ -5914,6 +5914,11 @@ footer .copyright {
|
|||||||
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
|
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hover\:text-blue-700:hover {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(29 78 216 / var(--tw-text-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
.hover\:text-gray-600:hover {
|
.hover\:text-gray-600:hover {
|
||||||
--tw-text-opacity: 1;
|
--tw-text-opacity: 1;
|
||||||
color: rgb(75 85 99 / var(--tw-text-opacity, 1));
|
color: rgb(75 85 99 / var(--tw-text-opacity, 1));
|
||||||
@@ -6337,6 +6342,11 @@ footer .copyright {
|
|||||||
background-color: rgb(8 47 73 / var(--tw-bg-opacity, 1));
|
background-color: rgb(8 47 73 / var(--tw-bg-opacity, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dark\:hover\:text-blue-300:hover:is(.dark *) {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(147 197 253 / var(--tw-text-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
.dark\:hover\:text-blue-500:hover:is(.dark *) {
|
.dark\:hover\:text-blue-500:hover:is(.dark *) {
|
||||||
--tw-text-opacity: 1;
|
--tw-text-opacity: 1;
|
||||||
color: rgb(59 130 246 / var(--tw-text-opacity, 1));
|
color: rgb(59 130 246 / var(--tw-text-opacity, 1));
|
||||||
|
|||||||
@@ -7,5 +7,6 @@
|
|||||||
<a href="{% add_preserved_filters changelist_url %}" class="closelink">{% translate 'Close' %}</a>
|
<a href="{% add_preserved_filters changelist_url %}" class="closelink">{% translate 'Close' %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if generate_pdf %}<input type="submit" value="PDF File generieren" class="default" name="_generate_pdf">{% endif %}
|
{% if generate_pdf %}<input type="submit" value="PDF File generieren" class="default" name="_generate_pdf">{% endif %}
|
||||||
|
{% if generate_rental_pdf %}<input type="submit" value="PDF File generieren" class="default" name="_generate_rental_pdf">{% endif %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
{% endblock submit-row %}
|
{% endblock submit-row %}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="{% url 'rental:rentalitem_detail' elem.data.value %}"
|
href="{% url 'rental:rentalitem_detail' elem.data.value %}"
|
||||||
class="text-gray-700 dark:text-gray-200"
|
class="text-gray-700 dark:text-gray-200 underline hover:text-blue-700 dark:hover:text-blue-300"
|
||||||
>{{ elem.choice_label }}</a>
|
>{{ elem.choice_label }}</a>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
{% if object.description %}
|
{% if object.description %}
|
||||||
<div class="col-span-full">
|
<div class="col-span-full">
|
||||||
<label>
|
<label>
|
||||||
<h2 class="text-xl font-black text-gray-700 dark:text-gray-200">Beschreibung</h2>
|
<h2 class="font-black text-gray-700 dark:text-gray-200">Beschreibung</h2>
|
||||||
<p class="text-gray-700 dark:text-gray-200">{{ object.description|safe }}</p>
|
<p class="text-gray-700 dark:text-gray-200">{{ object.description|safe }}</p>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user