From 91ceaaf9f9763f619781eeb31145090a413f0cef Mon Sep 17 00:00:00 2001
From: "marius.klein"
Date: Sun, 19 Jan 2025 10:11:27 +0100
Subject: [PATCH] members/excursion: select invoice for sjr application form
(#103)
Closes #61
Adds to the generate sjr application process a form in order to select one invoice.
Reviewed-on: https://git.jdav-hd.merten.dev/digitales/kompass/pulls/103
Co-authored-by: marius.klein
Co-committed-by: marius.klein
---
jdav_web/members/admin.py | 49 ++++++++++++++---
.../members/locale/de/LC_MESSAGES/django.po | 32 +++++++++++-
jdav_web/members/pdf.py | 14 +++--
.../admin/generate_sjr_application.html | 52 +++++++++++++++++++
4 files changed, 133 insertions(+), 14 deletions(-)
create mode 100644 jdav_web/members/templates/admin/generate_sjr_application.html
diff --git a/jdav_web/members/admin.py b/jdav_web/members/admin.py
index 56ca9c0..83f9517 100644
--- a/jdav_web/members/admin.py
+++ b/jdav_web/members/admin.py
@@ -981,6 +981,15 @@ class GenerateSeminarReportForm(forms.Form):
widget=CheckboxInput(attrs={'style': 'display: inherit'}),
required=False)
+class GenerateSjrForm(forms.Form):
+
+ def __init__(self, *args, **kwargs):
+ self.attachments = kwargs.pop('attachments')
+
+ super(GenerateSjrForm,self).__init__(*args,**kwargs)
+ self.fields['invoice'] = forms.ChoiceField(choices=self.attachments, label=_('Invoice'))
+
+
class FreizeitAdmin(CommonAdminMixin, nested_admin.NestedModelAdmin):
#inlines = [MemberOnListInline, LJPOnListInline, StatementOnListInline]
@@ -1077,17 +1086,41 @@ class FreizeitAdmin(CommonAdminMixin, nested_admin.NestedModelAdmin):
return serve_pdf(fp)
return self.render_seminar_report_options(request, memberlist, GenerateSeminarReportForm())
seminar_report.short_description = _('Generate seminar report')
-
+
+ def render_sjr_options(self, request, memberlist, form):
+ context = dict(self.admin_site.each_context(request),
+ title=_('Generate SJR application'),
+ opts=self.opts,
+ memberlist=memberlist,
+ form=form,
+ object=memberlist)
+ return render(request, 'admin/generate_sjr_application.html', context=context)
+
def sjr_application(self, request, memberlist):
- if not self.may_view_excursion(request, memberlist):
- return self.not_allowed_view(request, memberlist)
- context = memberlist.sjr_application_fields()
if hasattr(memberlist, 'statement'):
- attachments = [b.proof.path for b in memberlist.statement.bill_set.all() if b.proof]
+ attachment_names = [f"{b.short_description}: {b.explanation} ({b.amount:.2f}€)" for b in memberlist.statement.bill_set.all() if b.proof]
+ attachment_paths = [b.proof.path for b in memberlist.statement.bill_set.all() if b.proof]
else:
- attachments = []
- title = memberlist.ljpproposal.title if hasattr(memberlist, 'ljpproposal') else memberlist.name
- return fill_pdf_form(title + "_SJR_Antrag", 'members/sjr_template.pdf', context, attachments)
+ attachment_names = []
+ attachment_paths = []
+ attachments = zip(attachment_paths, attachment_names)
+
+ if not self.may_view_excursion(request, memberlist):
+ return self.not_allowed_view(request, memberlist)
+ if "apply" in request.POST:
+ form = GenerateSjrForm(request.POST, attachments=attachments)
+ if not form.is_valid():
+ messages.error(request, _('Please select an invoice.'))
+ return self.render_sjr_options(request, memberlist, form)
+
+ selected_attachments = [form.cleaned_data['invoice']]
+ context = memberlist.sjr_application_fields()
+ title = memberlist.ljpproposal.title if hasattr(memberlist, 'ljpproposal') else memberlist.name
+
+ return fill_pdf_form(title + "_SJR_Antrag", 'members/sjr_template.pdf', context, selected_attachments)
+
+ return self.render_sjr_options(request, memberlist, GenerateSjrForm(attachments=attachments))
+
sjr_application.short_description = _('Generate SJR application')
def finance_overview(self, request, memberlist):
diff --git a/jdav_web/members/locale/de/LC_MESSAGES/django.po b/jdav_web/members/locale/de/LC_MESSAGES/django.po
index f52f21e..f50cb29 100644
--- a/jdav_web/members/locale/de/LC_MESSAGES/django.po
+++ b/jdav_web/members/locale/de/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-01-18 23:22+0100\n"
+"POT-Creation-Date: 2025-01-19 10:08+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -341,6 +341,10 @@ msgstr "Modus"
msgid "Prepend V32"
msgstr "V32 Formblatt einfügen"
+#: members/admin.py
+msgid "Invoice"
+msgstr "Beleg"
+
#: members/admin.py
msgid ""
"General information on your excursion. These are partly relevant for the "
@@ -380,10 +384,14 @@ msgstr ""
"Der vollständiger Modus ist nur verfügbar, wenn der Seminarbericht "
"ausgefüllt ist. "
-#: members/admin.py
+#: members/admin.py members/templates/admin/generate_sjr_application.html
msgid "Generate SJR application"
msgstr "SJR Antrag erstellen"
+#: members/admin.py
+msgid "Please select an invoice."
+msgstr "Bitte wähle einen Beleg aus."
+
#: members/admin.py
msgid "No statement found. Please add a statement and then retry."
msgstr ""
@@ -1021,6 +1029,7 @@ msgstr "Fortbildungen"
#: members/templates/admin/demote_to_waiter.html
#: members/templates/admin/freizeit_finance_overview.html
#: members/templates/admin/generate_seminar_report.html
+#: members/templates/admin/generate_sjr_application.html
#: members/templates/admin/invite_as_user.html
#: members/templates/admin/invite_for_group.html
#: members/templates/admin/invite_for_group_text.html
@@ -1045,6 +1054,7 @@ msgstr "Zurück auf die Warteliste setzen"
#: members/templates/admin/demote_to_waiter.html
#: members/templates/admin/freizeit_finance_overview.html
#: members/templates/admin/generate_seminar_report.html
+#: members/templates/admin/generate_sjr_application.html
#: members/templates/admin/invite_as_user.html
#: members/templates/admin/invite_for_group.html
#: members/templates/admin/invite_selected_as_user.html
@@ -1301,9 +1311,27 @@ msgstr ""
"Felder im Formblatt selbst aus und unterschreibe das PDF."
#: members/templates/admin/generate_seminar_report.html
+#: members/templates/admin/generate_sjr_application.html
msgid "Generate"
msgstr "Erstellen"
+#: members/templates/admin/generate_sjr_application.html
+msgid "Here you can generate an allowance application for the SJR."
+msgstr "Hier kannst du einen SJR-Zuschussantrag erstellen."
+
+#: members/templates/admin/generate_sjr_application.html
+msgid ""
+"The application needs to be complemented with an invoice from the trip as "
+"proof."
+msgstr ""
+"An den Antrag muss ein Ausgabenbeleg angehängt werden, der beweist, dass die "
+"Aktivität stattgefunden hat."
+
+#: members/templates/admin/generate_sjr_application.html
+msgid ""
+"Please send this application form to the jdav finance officer via email."
+msgstr "Bitte sende diesen Antrag an den/die JDAV-Finanzwart*in per E-Mail."
+
#: members/templates/admin/invite_as_user.html
#, python-format
msgid ""
diff --git a/jdav_web/members/pdf.py b/jdav_web/members/pdf.py
index a273b95..00a9877 100644
--- a/jdav_web/members/pdf.py
+++ b/jdav_web/members/pdf.py
@@ -100,12 +100,18 @@ def fill_pdf_form(name, template_path, fields, attachments=[], save_only=False):
for fp in attachments:
try:
- img = Image.open(fp)
- img_pdf = BytesIO()
- img.save(img_pdf, "pdf")
+ if fp.endswith(".pdf"):
+ # append pdf directly
+ img_pdf = PdfReader(fp)
+ else:
+ # convert ensures that png files with an alpha channel can be appended
+ img = Image.open(fp).convert("RGB")
+ img_pdf = BytesIO()
+ img.save(img_pdf, "pdf")
writer.append(img_pdf)
- except:
+ except Exception as e:
print("Could not add image", fp)
+ print(e)
with open(media_path(filename_pdf), 'wb') as output_stream:
writer.write(output_stream)
diff --git a/jdav_web/members/templates/admin/generate_sjr_application.html b/jdav_web/members/templates/admin/generate_sjr_application.html
new file mode 100644
index 0000000..98f6f53
--- /dev/null
+++ b/jdav_web/members/templates/admin/generate_sjr_application.html
@@ -0,0 +1,52 @@
+{% extends "admin/base_site.html" %}
+{% load i18n admin_urls static %}
+
+{% block extrahead %}
+ {{ block.super }}
+ {{ media }}
+
+
+
+{% endblock %}
+
+{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} invite-waiter
+{% endblock %}
+
+{% block breadcrumbs %}
+
+{% endblock %}
+
+{% block content %}
+
+{% blocktrans %}Here you can generate an allowance application for the SJR.{% endblocktrans %}
+
+
+{% blocktrans %}The application needs to be complemented with an invoice from the trip as proof.{% endblocktrans %}
+
+
+
+
+ {% blocktrans %}Please send this application form to the jdav finance officer via email.{% endblocktrans %}
+
+
+
+
+
+ {% translate "Cancel" %}
+
+
+
+{% endblock %}