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: #103
Co-authored-by: marius.klein <marius.klein@alpenverein-heidelberg.de>
Co-committed-by: marius.klein <marius.klein@alpenverein-heidelberg.de>
pull/104/head
marius.klein 11 months ago committed by Christian Merten
parent 98a03e4abd
commit 91ceaaf9f9

@ -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]
@ -1078,16 +1087,40 @@ class FreizeitAdmin(CommonAdminMixin, nested_admin.NestedModelAdmin):
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 hasattr(memberlist, 'statement'):
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:
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()
if hasattr(memberlist, 'statement'):
attachments = [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)
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):

@ -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 <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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 ""

@ -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)
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)

@ -0,0 +1,52 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static %}
{% block extrahead %}
{{ block.super }}
{{ media }}
<script src="{% static 'admin/js/cancel.js' %}" async></script>
<script type="text/javascript" src="{% static "admin/js/vendor/jquery/jquery.js" %}"></script>
<script type="text/javascript" src="{% static "admin/js/jquery.init.js" %}"></script>
{% endblock %}
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} invite-waiter
{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
&rsaquo; {% translate 'Generate SJR application' %}
</div>
{% endblock %}
{% block content %}
<p>
{% blocktrans %}Here you can generate an allowance application for the SJR.{% endblocktrans %}
</p>
<p>
{% blocktrans %}The application needs to be complemented with an invoice from the trip as proof.{% endblocktrans %}
</p>
<form action="" method="post">
{% csrf_token %}
<p>
<table>
{{ form }}
</table>
</p>
<p>
{% blocktrans %}Please send this application form to the jdav finance officer via email.{% endblocktrans %}
</p>
<br>
<input type="hidden" name="action" value="sjr_application">
<input type="hidden" name="sjr_application">
<input class="default" style="color: $default-link-color" type="submit" name="apply"
value="{% translate 'Generate' %}">
<a href="#" class="button cancel-link">{% translate "Cancel" %}</a>
</form>
{% endblock %}
Loading…
Cancel
Save