finance/statement: show IBAN validity in submit views (#104)

* Add new attribute `iban_valid` to `Member`.
* Submit pages of regular expenses and activity expenses now include an overview which people with expenses have a valid IBAN.
* Add a note that IBAN validity should be checked manually before submitting.

Right now, no technical barriers are in place that prevent missing/wrong IBANs when a statement is submitted. This is intentional to preserve maximal flexibility.

In a follow-up PR, one could add custom warning messages on top of page when some IBANs are invalid.

Reviewed-on: #104
Reviewed-by: Christian Merten <christian@merten.dev>
Co-authored-by: marius.klein <marius.klein@alpenverein-heidelberg.de>
Co-committed-by: marius.klein <marius.klein@alpenverein-heidelberg.de>
toml-configuration-with-templates
marius.klein 11 months ago committed by Christian Merten
parent a2cbae2c8e
commit e7dcbb47ab

@ -89,12 +89,25 @@ class StatementUnSubmittedAdmin(CommonAdminMixin, admin.ModelAdmin):
messages.success(request,
_("Successfully submited %(name)s. The finance department will notify the requestors as soon as possible.") % {'name': str(statement)})
return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name)))
context = dict(self.admin_site.each_context(request),
title=_('Submit statement'),
if statement.excursion:
memberlist = statement.excursion
context = dict(self.admin_site.each_context(request),
title=_('Finance overview'),
opts=self.opts,
statement=statement)
return render(request, 'admin/submit_statement.html', context=context)
memberlist=memberlist,
object=memberlist,
participant_count=memberlist.participant_count,
ljp_contributions=memberlist.potential_ljp_contributions,
total_relative_costs=memberlist.total_relative_costs,
**memberlist.statement.template_context())
return render(request, 'admin/freizeit_finance_overview.html', context=context)
else:
context = dict(self.admin_site.each_context(request),
title=_('Submit statement'),
opts=self.opts,
statement=statement)
return render(request, 'admin/submit_statement.html', context=context)
class TransactionOnSubmittedStatementInline(admin.TabularInline):

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-19 11:40+0100\n"
"POT-Creation-Date: 2025-01-19 14:26+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"
@ -169,7 +169,7 @@ msgstr "Geldtöpfe"
msgid "Short description"
msgstr "Kurzbeschreibung"
#: finance/models.py
#: finance/models.py finance/templates/admin/submit_statement.html
msgid "Explanation"
msgstr "Erklärung"
@ -208,7 +208,7 @@ msgstr "Preis pro Nacht"
#: finance/models.py
msgid "Submitted"
msgstr "Eingericht"
msgstr "Eingereicht"
#: finance/models.py
msgid "Submitted on"
@ -291,10 +291,11 @@ msgstr "Bezahlte Abrechnungen"
#: finance/models.py finance/templates/admin/confirmed_statement.html
#: finance/templates/admin/overview_submitted_statement.html
#: finance/templates/admin/submit_statement.html
msgid "Amount"
msgstr "Betrag"
#: finance/models.py
#: finance/models.py finance/templates/admin/submit_statement.html
msgid "Paid by"
msgstr "Bezahlt von"
@ -533,6 +534,18 @@ msgstr "Einreichen"
msgid "Submit to the finance department"
msgstr "Beim Finanzreferat einreichen"
#: finance/templates/admin/submit_statement.html
msgid ""
"Please check if all expenses are documented correctly and if all payers have "
"a valid account code."
msgstr ""
"Bitte überprüfe, ob alle Ausgaben korrekt erfasst sind und ob alle "
"auslegenden Personen eine gültige IBAN haben."
#: finance/templates/admin/submit_statement.html
msgid "IBAN valid"
msgstr "IBAN gültig"
#: finance/templates/admin/submit_statement.html
msgid ""
"Do you want to submit the statement for further processing by the finance "

@ -1,5 +1,6 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static %}
{% load overview_extras %}
{% block extrahead %}
{{ block.super }}
@ -40,7 +41,7 @@
{{ bill.amount }}€.
</td>
<td>
{{ bill.costs_covered }}
{{ bill.costs_covered|render_bool }}
</td>
</tr>
{% endfor %}

@ -1,5 +1,6 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static %}
{% load overview_extras %}
{% block extrahead %}
{{ block.super }}
@ -24,6 +25,35 @@
{% block content %}
<h2>{% translate "Submit to the finance department" %}</h2>
<p>{% translate "Please check if all expenses are documented correctly and if all payers have a valid account code." %}</p>
<table>
<th>
<td>{% trans "Explanation" %}</td>
<td>{% trans "Amount" %}</td>
<td>{% trans "Paid by" %}</td>
<td>{% trans "IBAN valid" %}</td>
</th>
{% for bill in statement.bill_set.all %}
<tr>
<td>
{{bill.short_description}}
</td>
<td>
{{bill.explanation}}
</td>
<td>
{{ bill.amount }}€
</td>
<td>
{{ bill.paid_by.name }}
</td>
<td>
{{ bill.paid_by.iban_valid|render_bool }}
</td>
</tr>
{% endfor %}
</table>
<p>
{% trans "Do you want to submit the statement for further processing by the finance department? If you proceed, no further changes to the statement are possible." %}
</p>

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-19 10:08+0100\n"
"POT-Creation-Date: 2025-01-19 19:16+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"
@ -411,7 +411,7 @@ msgid ""
"Successfully submited statement. The finance department will notify you as "
"soon as possible."
msgstr ""
"Abrechnung erfolgreich eingericht. Die Finanzabteilung wird sich bei dir so "
"Abrechnung erfolgreich eingereicht. Die Finanzabteilung wird sich bei dir so "
"schnell wie möglich melden."
#: members/admin.py members/templates/admin/freizeit_finance_overview.html
@ -1089,6 +1089,14 @@ msgstr "Erklärung"
msgid "Amount"
msgstr "Betrag"
#: members/templates/admin/freizeit_finance_overview.html
msgid "Paid by"
msgstr "Bezahlt von"
#: members/templates/admin/freizeit_finance_overview.html
msgid "IBAN valid"
msgstr "IBAN gültig"
#: members/templates/admin/freizeit_finance_overview.html
#, python-format
msgid "The total expected expenses are %(total_bills_theoretic)s €."
@ -1229,14 +1237,17 @@ msgstr "Abrechnung einreichen"
msgid ""
"Did you already complete this excursion? If yes, please check if all listed "
"expenses are correct\n"
"and then submit the statement for processing by the finance department. If "
"you proceed,\n"
"and people who want their money back have valid bank account numbers. Then "
"submit the statement for processing by the finance department. If you "
"proceed,\n"
"no further changes to the statement are possible."
msgstr ""
"Hat die Ausfahrt bereits stattgefunden? Wenn ja, prüfe bitte ob alle "
"aufgelisteten Kosten korrekt sind und reiche deine Abrechnung dann beim "
"Finanzreferat ein. Wenn du fortschreitest sind keine weiteren Änderungen an "
"der Abrechnung mehr möglich."
"aufgelisteten Kosten korrekt sind, alle notwendigen Belege hochgeladen sind "
"und ob alle Personen, die Geld ausbezahlt bekommen sollen, eine gültige IBAN "
"haben. Reiche deine Abrechnung dann beim Finanzreferat ein. Wenn du "
"fortschreitest, sind keine weiteren Änderungen an der Abrechnung mehr "
"möglich."
#: members/templates/admin/freizeit_finance_overview.html
msgid "Submit"

@ -30,6 +30,7 @@ from contrib.rules import memberize_user, has_global_perm
from utils import cvt_to_decimal
from dateutil.relativedelta import relativedelta
from schwifty import IBAN
def generate_random_key():
return uuid.uuid4().hex
@ -342,6 +343,10 @@ class Member(Person):
"""Returning the whole place (plz + town)"""
return "{0} {1}".format(self.plz, self.town)
@property
def iban_valid(self):
return IBAN(self.iban, allow_invalid=True).is_valid
@property
def address(self):
"""Returning the whole address"""
@ -1210,7 +1215,7 @@ class Freizeit(CommonModel):
if not self.statement:
return 0
total_costs = self.statement.total_bills_theoretic
total_contributions = self.statement.total_staff + self.potential_ljp_contributions
total_contributions = self.statement.total_subsidies + self.potential_ljp_contributions
return total_costs - total_contributions
@property

@ -1,5 +1,6 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static %}
{% load overview_extras %}
{% block extrahead %}
{{ block.super }}
@ -38,6 +39,8 @@ cost plan!
<th>
<td>{% trans "Explanation" %}</td>
<td>{% trans "Amount" %}</td>
<td>{% trans "Paid by" %}</td>
<td>{% trans "IBAN valid" %}</td>
</th>
{% for bill in memberlist.statement.bill_set.all %}
<tr>
@ -50,6 +53,12 @@ cost plan!
<td>
{{ bill.amount }}€
</td>
<td>
{{ bill.paid_by.name }}
</td>
<td>
{{ bill.paid_by.iban_valid|render_bool }}
</td>
</tr>
{% endfor %}
</table>
@ -78,21 +87,29 @@ cost plan!
</p>
<p>
{% blocktrans %}The allowance of {{ allowance_per_yl }}€ per person is configured to be paid to:{% endblocktrans %}
<ul>
<table>
<th>
<td>{% trans "IBAN valid" %}</td>
</th>
{% for member in memberlist.statement.allowance_to.all %}
<li>
{{ member.name }}
</li>
<tr>
<td>{{ member.name }}</td>
<td>{{ member.iban_valid|render_bool }}</td>
</tr>
{% endfor %}
</ul>
</table>
</p>
<p>
{% blocktrans %}The subsidies for night and transportation costs of {{ total_subsidies }}€ is configured to be paid to:{% endblocktrans %}
<ul>
<li>
{{ memberlist.statement.subsidy_to.name }}
</li>
</ul>
<table>
<th>
<td>{% trans "IBAN valid" %}</td>
</th>
<tr>
<td>{{ memberlist.statement.subsidy_to.name }}</td>
<td>{{ memberlist.statement.subsidy_to.iban_valid|render_bool }}</td>
</tr>
</table>
</p>
{% if not memberlist.statement.allowance_to_valid %}
<p>
@ -128,7 +145,7 @@ you may obtain up to 25€ times {{ duration }} days for {{ participant_count }}
{% trans "Contributions by the association" %}
</td>
<td>
-{{ total_staff }}€
-{{ total_subsidies }}€
</td>
</tr>
<tr>
@ -162,7 +179,7 @@ excursions main page, you can generate a template for a seminar report.{% endblo
<h3>{% trans "Submit statement" %}</h3>
<p>
{% blocktrans %}Did you already complete this excursion? If yes, please check if all listed expenses are correct
and then submit the statement for processing by the finance department. If you proceed,
and people who want their money back have valid bank account numbers. Then submit the statement for processing by the finance department. If you proceed,
no further changes to the statement are possible.{% endblocktrans %}
</p>

@ -1,4 +1,5 @@
from django import template
from django.utils.html import format_html
register = template.Library()
@ -15,3 +16,19 @@ def has_attendee_wrapper(klettertreff, member):
@register.simple_tag
def has_jugendleiter_wrapper(klettertreff, jugendleiter):
return blToColor(klettertreff.has_jugendleiter(jugendleiter))
@register.filter
def render_bool(boolean_value):
if not isinstance(boolean_value, bool):
raise ValueError(f"""Custom Filter 'render_bool': Supplied value '{boolean_value}' is not bool, but {type(boolean_value)}.""")
if boolean_value: # True is a green tick
color = "#bcd386"
htmlclass = "icon-tick"
else: # False is a red cross
color = "#dba4a4"
htmlclass = "icon-cross"
return format_html(f"""<span style="font-weight: bold; color: {color};"
class="{htmlclass}"></span>""")
Loading…
Cancel
Save