members/excursion: add finance overview

pull/73/head
Christian Merten 1 year ago
parent 5734d41a23
commit 79b5389107
Signed by: christian.merten
GPG Key ID: D953D69721B948B3

@ -9,6 +9,7 @@ from django.shortcuts import render
from django.conf import settings from django.conf import settings
from contrib.admin import CommonAdminInlineMixin, CommonAdminMixin from contrib.admin import CommonAdminInlineMixin, CommonAdminMixin
from utils import get_member
from rules.contrib.admin import ObjectPermissionsModelAdmin from rules.contrib.admin import ObjectPermissionsModelAdmin
@ -218,23 +219,7 @@ class StatementSubmittedAdmin(admin.ModelAdmin):
opts=self.opts, opts=self.opts,
statement=statement, statement=statement,
transaction_issues=statement.transaction_issues, transaction_issues=statement.transaction_issues,
total_bills=statement.total_bills, **statement.template_context())
total=statement.total)
if statement.excursion is not None:
context = dict(context,
nights=statement.excursion.night_count,
price_per_night=statement.real_night_cost,
duration=statement.excursion.duration,
staff_count=statement.real_staff_count,
kilometers_traveled=statement.excursion.kilometers_traveled,
means_of_transport=statement.excursion.get_tour_approach(),
euro_per_km=statement.euro_per_km,
allowance_per_day=settings.ALLOWANCE_PER_DAY,
nights_per_yl=statement.nights_per_yl,
allowance_per_yl=statement.allowance_per_yl,
transportation_per_yl=statement.transportation_per_yl,
total_per_yl=statement.total_per_yl,
total_staff=statement.total_staff)
return render(request, 'admin/overview_submitted_statement.html', context=context) return render(request, 'admin/overview_submitted_statement.html', context=context)
@ -325,10 +310,3 @@ class BillAdmin(admin.ModelAdmin):
list_display = ['__str__', 'statement', 'short_description', 'pretty_amount', 'paid_by', 'refunded'] list_display = ['__str__', 'statement', 'short_description', 'pretty_amount', 'paid_by', 'refunded']
list_filter = ('statement', 'paid_by', 'refunded') list_filter = ('statement', 'paid_by', 'refunded')
search_fields = ('reference', 'statement') search_fields = ('reference', 'statement')
def get_member(request):
if not hasattr(request.user, 'member'):
return None
else:
return request.user.member

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-24 01:34+0100\n" "POT-Creation-Date: 2024-11-30 03:54+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,12 +18,12 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: finance/admin.py:75 #: finance/admin.py:76
#, python-format #, python-format
msgid "%(name)s is already submitted." msgid "%(name)s is already submitted."
msgstr "%(name)s ist bereits eingereicht." msgstr "%(name)s ist bereits eingereicht."
#: finance/admin.py:81 #: finance/admin.py:82
#, python-format #, python-format
msgid "" msgid ""
"Successfully submited %(name)s. The finance department will notify the " "Successfully submited %(name)s. The finance department will notify the "
@ -32,23 +32,23 @@ msgstr ""
"Rechnung %(name)s erfolgreich eingereicht. Das Finanzreferat wird auf dich " "Rechnung %(name)s erfolgreich eingereicht. Das Finanzreferat wird auf dich "
"sobald wie möglich zukommen." "sobald wie möglich zukommen."
#: finance/admin.py:84 #: finance/admin.py:85
msgid "Submit statement" msgid "Submit statement"
msgstr "Rechnung einreichen" msgstr "Rechnung einreichen"
#: finance/admin.py:161 #: finance/admin.py:162
#, python-format #, python-format
msgid "%(name)s is not yet submitted." msgid "%(name)s is not yet submitted."
msgstr "%(name)s ist noch nicht eingereicht." msgstr "%(name)s ist noch nicht eingereicht."
#: finance/admin.py:168 #: finance/admin.py:169
#, python-format #, python-format
msgid "An error occured while trying to confirm %(name)s. Please try again." msgid "An error occured while trying to confirm %(name)s. Please try again."
msgstr "" msgstr ""
"Beim Abwickeln von %(name)s ist ein Fehler aufgetreten. Bitte versuche es " "Beim Abwickeln von %(name)s ist ein Fehler aufgetreten. Bitte versuche es "
"erneut." "erneut."
#: finance/admin.py:172 #: finance/admin.py:173
#, python-format #, python-format
msgid "" msgid ""
"Successfully confirmed %(name)s. I hope you executed the associated " "Successfully confirmed %(name)s. I hope you executed the associated "
@ -57,11 +57,11 @@ msgstr ""
"Erfolgreich %(name)s abgewickelt. Ich hoffe du hast die zugehörigen " "Erfolgreich %(name)s abgewickelt. Ich hoffe du hast die zugehörigen "
"Überweisungen ausgeführt, ich werde dich nicht nochmal erinnern." "Überweisungen ausgeführt, ich werde dich nicht nochmal erinnern."
#: finance/admin.py:179 #: finance/admin.py:180
msgid "Statement confirmed" msgid "Statement confirmed"
msgstr "Abrechnung abgewickelt" msgstr "Abrechnung abgewickelt"
#: finance/admin.py:185 #: finance/admin.py:186
msgid "" msgid ""
"Transactions do not match the covered expenses. Please correct the mistakes " "Transactions do not match the covered expenses. Please correct the mistakes "
"listed below." "listed below."
@ -69,19 +69,19 @@ msgstr ""
"Überweisungen stimmen nicht mit den übernommenen Kosten überein. Bitte " "Überweisungen stimmen nicht mit den übernommenen Kosten überein. Bitte "
"korrigiere die unten aufgeführten Fehler." "korrigiere die unten aufgeführten Fehler."
#: finance/admin.py:190 #: finance/admin.py:191
msgid "Some transactions have no ledger configured. Please fill in the gaps." msgid "Some transactions have no ledger configured. Please fill in the gaps."
msgstr "" msgstr ""
"Manche Überweisungen haben kein Geldtopf eingestellt. Bitte trage das nach." "Manche Überweisungen haben kein Geldtopf eingestellt. Bitte trage das nach."
#: finance/admin.py:199 #: finance/admin.py:200
#, python-format #, python-format
msgid "Successfully rejected %(name)s. The requestor can reapply, when needed." msgid "Successfully rejected %(name)s. The requestor can reapply, when needed."
msgstr "" msgstr ""
"Die Rechnung %(name)s wurde abgelehnt. Die Person kann die Rechnung erneut " "Die Rechnung %(name)s wurde abgelehnt. Die Person kann die Rechnung erneut "
"einstellen, wenn es benötigt wird." "einstellen, wenn es benötigt wird."
#: finance/admin.py:206 #: finance/admin.py:207
#, python-format #, python-format
msgid "" msgid ""
"%(name)s already has transactions. Please delete them first, if you want to " "%(name)s already has transactions. Please delete them first, if you want to "
@ -90,12 +90,12 @@ msgstr ""
"%(name)s hat bereits Überweisungen. Bitte lösche diese zunächst, bevor du " "%(name)s hat bereits Überweisungen. Bitte lösche diese zunächst, bevor du "
"neue generierst." "neue generierst."
#: finance/admin.py:211 #: finance/admin.py:212
#, python-format #, python-format
msgid "Successfully generated transactions for %(name)s" msgid "Successfully generated transactions for %(name)s"
msgstr "Automatisch Überweisungsträger für %(name)s generiert." msgstr "Automatisch Überweisungsträger für %(name)s generiert."
#: finance/admin.py:214 #: finance/admin.py:215
#, python-format #, python-format
msgid "" msgid ""
"Error while generating transactions for %(name)s. Do all bills have a payer?" "Error while generating transactions for %(name)s. Do all bills have a payer?"
@ -103,28 +103,28 @@ msgstr ""
"Fehler beim Erzeugen der Überweisungsträger für %(name)s. Sind für alle " "Fehler beim Erzeugen der Überweisungsträger für %(name)s. Sind für alle "
"Quittungen eine bezahlende Person eingestellt? " "Quittungen eine bezahlende Person eingestellt? "
#: finance/admin.py:217 #: finance/admin.py:218
msgid "View submitted statement" msgid "View submitted statement"
msgstr "Eingereichte Abrechnung einsehen" msgstr "Eingereichte Abrechnung einsehen"
#: finance/admin.py:245 #: finance/admin.py:230
#, python-format #, python-format
msgid "Successfully reduced transactions for %(name)s." msgid "Successfully reduced transactions for %(name)s."
msgstr "Überweisungsträger für %(name)s minimiert." msgstr "Überweisungsträger für %(name)s minimiert."
#: finance/admin.py:289 #: finance/admin.py:274
#, python-format #, python-format
msgid "%(name)s is not yet confirmed." msgid "%(name)s is not yet confirmed."
msgstr "%(name)s ist noch nicht bestätigt." msgstr "%(name)s ist noch nicht bestätigt."
#: finance/admin.py:298 #: finance/admin.py:283
#, python-format #, python-format
msgid "Successfully unconfirmed %(name)s. I hope you know what you are doing." msgid "Successfully unconfirmed %(name)s. I hope you know what you are doing."
msgstr "" msgstr ""
"Erfolgreich die Bestätigung von %(name)s zurückgenommen. Ich hoffe du weißt " "Erfolgreich die Bestätigung von %(name)s zurückgenommen. Ich hoffe du weißt "
"was du machst." "was du machst."
#: finance/admin.py:303 finance/templates/admin/unconfirm_statement.html:26 #: finance/admin.py:288 finance/templates/admin/unconfirm_statement.html:26
msgid "Unconfirm statement" msgid "Unconfirm statement"
msgstr "Bestätigung zurücknehmen" msgstr "Bestätigung zurücknehmen"
@ -132,165 +132,165 @@ msgstr "Bestätigung zurücknehmen"
msgid "Finance" msgid "Finance"
msgstr "Finanzen" msgstr "Finanzen"
#: finance/models.py:19 #: finance/models.py:20
msgid "Name" msgid "Name"
msgstr "Name" msgstr "Name"
#: finance/models.py:25 finance/models.py:441 finance/models.py:465 #: finance/models.py:26 finance/models.py:466 finance/models.py:490
#: finance/templates/admin/confirmed_statement.html:38 #: finance/templates/admin/confirmed_statement.html:38
#: finance/templates/admin/overview_submitted_statement.html:100 #: finance/templates/admin/overview_submitted_statement.html:100
msgid "Ledger" msgid "Ledger"
msgstr "Geldtopf" msgstr "Geldtopf"
#: finance/models.py:26 #: finance/models.py:27
msgid "Ledgers" msgid "Ledgers"
msgstr "Geldtöpfe" msgstr "Geldtöpfe"
#: finance/models.py:46 finance/models.py:384 finance/models.py:464 #: finance/models.py:47 finance/models.py:409 finance/models.py:489
msgid "Short description" msgid "Short description"
msgstr "Kurzbeschreibung" msgstr "Kurzbeschreibung"
#: finance/models.py:49 finance/models.py:385 #: finance/models.py:50 finance/models.py:410
msgid "Explanation" msgid "Explanation"
msgstr "Erklärung" msgstr "Erklärung"
#: finance/models.py:51 #: finance/models.py:52
msgid "Associated excursion" msgid "Associated excursion"
msgstr "Zugehörige Ausfahrt" msgstr "Zugehörige Ausfahrt"
#: finance/models.py:56 #: finance/models.py:57
msgid "Price per night" msgid "Price per night"
msgstr "Preis pro Nacht" msgstr "Preis pro Nacht"
#: finance/models.py:58 #: finance/models.py:59
msgid "Submitted" msgid "Submitted"
msgstr "Eingericht" msgstr "Eingericht"
#: finance/models.py:59 #: finance/models.py:60
msgid "Submitted on" msgid "Submitted on"
msgstr "Eingereicht am" msgstr "Eingereicht am"
#: finance/models.py:60 #: finance/models.py:61
msgid "Confirmed" msgid "Confirmed"
msgstr "Abgewickelt" msgstr "Abgewickelt"
#: finance/models.py:61 finance/models.py:448 #: finance/models.py:62 finance/models.py:473
msgid "Paid on" msgid "Paid on"
msgstr "Bezahlt am" msgstr "Bezahlt am"
#: finance/models.py:63 #: finance/models.py:64
msgid "Created by" msgid "Created by"
msgstr "Erstellt von" msgstr "Erstellt von"
#: finance/models.py:68 #: finance/models.py:69
msgid "Submitted by" msgid "Submitted by"
msgstr "Eingereicht bei" msgstr "Eingereicht bei"
#: finance/models.py:73 finance/models.py:449 #: finance/models.py:74 finance/models.py:474
msgid "Authorized by" msgid "Authorized by"
msgstr "Autorisiert von" msgstr "Autorisiert von"
#: finance/models.py:80 finance/models.py:383 finance/models.py:444 #: finance/models.py:81 finance/models.py:408 finance/models.py:469
msgid "Statement" msgid "Statement"
msgstr "Abrechnung" msgstr "Abrechnung"
#: finance/models.py:81 #: finance/models.py:82
msgid "Statements" msgid "Statements"
msgstr "Abrechnungen" msgstr "Abrechnungen"
#: finance/models.py:96 #: finance/models.py:97
#, python-format #, python-format
msgid "Statement: %(excursion)s" msgid "Statement: %(excursion)s"
msgstr "Abrechnung: %(excursion)s" msgstr "Abrechnung: %(excursion)s"
#: finance/models.py:148 #: finance/models.py:149
msgid "Ready to confirm" msgid "Ready to confirm"
msgstr "Bereit zur Abwicklung" msgstr "Bereit zur Abwicklung"
#: finance/models.py:192 #: finance/models.py:193
#, python-format #, python-format
msgid "Compensation for %(excu)s" msgid "Compensation for %(excu)s"
msgstr "Entschädigung für %(excu)s" msgstr "Entschädigung für %(excu)s"
#: finance/models.py:325 #: finance/models.py:326
#: finance/templates/admin/overview_submitted_statement.html:78 #: finance/templates/admin/overview_submitted_statement.html:78
msgid "Total" msgid "Total"
msgstr "Gesamtbetrag" msgstr "Gesamtbetrag"
#: finance/models.py:338 #: finance/models.py:363
msgid "Statement in preparation" msgid "Statement in preparation"
msgstr "Abrechnung in Vorbereitung" msgstr "Abrechnung in Vorbereitung"
#: finance/models.py:339 #: finance/models.py:364
msgid "Statements in preparation" msgid "Statements in preparation"
msgstr "Abrechnungen in Vorbereitung" msgstr "Abrechnungen in Vorbereitung"
#: finance/models.py:358 #: finance/models.py:383
msgid "Submitted statement" msgid "Submitted statement"
msgstr "Eingereichte Abrechnung" msgstr "Eingereichte Abrechnung"
#: finance/models.py:359 #: finance/models.py:384
msgid "Submitted statements" msgid "Submitted statements"
msgstr "Eingereichte Abrechnungen" msgstr "Eingereichte Abrechnungen"
#: finance/models.py:375 #: finance/models.py:400
msgid "Paid statement" msgid "Paid statement"
msgstr "Bezahlte Abrechnung" msgstr "Bezahlte Abrechnung"
#: finance/models.py:376 #: finance/models.py:401
msgid "Paid statements" msgid "Paid statements"
msgstr "Bezahlte Abrechnungen" msgstr "Bezahlte Abrechnungen"
#: finance/models.py:388 #: finance/models.py:412 finance/models.py:426 finance/models.py:463
#: finance/templates/admin/confirmed_statement.html:36
#: finance/templates/admin/overview_submitted_statement.html:31
#: finance/templates/admin/overview_submitted_statement.html:98
msgid "Amount"
msgstr "Betrag"
#: finance/models.py:413
msgid "Paid by" msgid "Paid by"
msgstr "Bezahlt von" msgstr "Bezahlt von"
#: finance/models.py:390 #: finance/models.py:415
msgid "Covered" msgid "Covered"
msgstr "Übernommen" msgstr "Übernommen"
#: finance/models.py:391 #: finance/models.py:416
msgid "Refunded" msgid "Refunded"
msgstr "Ausgezahlt" msgstr "Ausgezahlt"
#: finance/models.py:393 #: finance/models.py:418
msgid "Proof" msgid "Proof"
msgstr "Beleg" msgstr "Beleg"
#: finance/models.py:401 finance/models.py:438 #: finance/models.py:429 finance/models.py:436 finance/models.py:449
#: finance/templates/admin/confirmed_statement.html:36
#: finance/templates/admin/overview_submitted_statement.html:31
#: finance/templates/admin/overview_submitted_statement.html:98
msgid "Amount"
msgstr "Betrag"
#: finance/models.py:404 finance/models.py:411 finance/models.py:424
msgid "Bill" msgid "Bill"
msgstr "Quittung" msgstr "Quittung"
#: finance/models.py:405 finance/models.py:412 finance/models.py:425 #: finance/models.py:430 finance/models.py:437 finance/models.py:450
#: finance/templates/admin/overview_submitted_statement.html:26 #: finance/templates/admin/overview_submitted_statement.html:26
msgid "Bills" msgid "Bills"
msgstr "Quittungen" msgstr "Quittungen"
#: finance/models.py:437 finance/templates/admin/confirmed_statement.html:37 #: finance/models.py:462 finance/templates/admin/confirmed_statement.html:37
#: finance/templates/admin/overview_submitted_statement.html:99 #: finance/templates/admin/overview_submitted_statement.html:99
msgid "Reference" msgid "Reference"
msgstr "Verwendungszweck" msgstr "Verwendungszweck"
#: finance/models.py:439 #: finance/models.py:464
msgid "Recipient" msgid "Recipient"
msgstr "Empfänger" msgstr "Empfänger"
#: finance/models.py:447 #: finance/models.py:472
msgid "Paid" msgid "Paid"
msgstr "Bezahlt" msgstr "Bezahlt"
#: finance/models.py:459 #: finance/models.py:484
msgid "Transaction" msgid "Transaction"
msgstr "Überweisung" msgstr "Überweisung"
#: finance/models.py:460 #: finance/models.py:485
#: finance/templates/admin/overview_submitted_statement.html:84 #: finance/templates/admin/overview_submitted_statement.html:84
msgid "Transactions" msgid "Transactions"
msgstr "Überweisungen" msgstr "Überweisungen"

@ -12,6 +12,7 @@ from django.conf import settings
import rules import rules
from contrib.models import CommonModel from contrib.models import CommonModel
from contrib.rules import has_global_perm from contrib.rules import has_global_perm
from utils import cvt_to_decimal
# Create your models here. # Create your models here.
@ -324,6 +325,30 @@ class Statement(CommonModel):
return "{}".format(self.total) return "{}".format(self.total)
total_pretty.short_description = _('Total') total_pretty.short_description = _('Total')
def template_context(self):
context = {
'total_bills': self.total_bills,
'total_bills_theoretic': self.total_bills_theoretic,
'total': self.total,
}
if self.excursion:
excursion_context = {
'nights': self.excursion.night_count,
'price_per_night': self.real_night_cost,
'duration': self.excursion.duration,
'staff_count': self.real_staff_count,
'kilometers_traveled': self.excursion.kilometers_traveled,
'means_of_transport': self.excursion.get_tour_approach(),
'euro_per_km': self.euro_per_km,
'allowance_per_day': settings.ALLOWANCE_PER_DAY,
'nights_per_yl': self.nights_per_yl,
'allowance_per_yl': self.allowance_per_yl,
'transportation_per_yl': self.transportation_per_yl,
'total_per_yl': self.total_per_yl,
'total_staff': self.total_staff,
}
return dict(context, **excursion_context)
class StatementUnSubmittedManager(models.Manager): class StatementUnSubmittedManager(models.Manager):
def get_queryset(self): def get_queryset(self):
@ -384,7 +409,7 @@ class Bill(CommonModel):
short_description = models.CharField(verbose_name=_('Short description'), max_length=30) short_description = models.CharField(verbose_name=_('Short description'), max_length=30)
explanation = models.TextField(verbose_name=_('Explanation'), blank=True) explanation = models.TextField(verbose_name=_('Explanation'), blank=True)
amount = models.DecimalField(max_digits=6, decimal_places=2, default=0) amount = models.DecimalField(verbose_name=_('Amount'), max_digits=6, decimal_places=2, default=0)
paid_by = models.ForeignKey(Member, verbose_name=_('Paid by'), null=True, paid_by = models.ForeignKey(Member, verbose_name=_('Paid by'), null=True,
on_delete=models.SET_NULL) on_delete=models.SET_NULL)
costs_covered = models.BooleanField(verbose_name=_('Covered'), default=False) costs_covered = models.BooleanField(verbose_name=_('Covered'), default=False)
@ -466,7 +491,3 @@ class Receipt(models.Model):
on_delete=models.CASCADE) on_delete=models.CASCADE)
amount = models.DecimalField(max_digits=6, decimal_places=2) amount = models.DecimalField(max_digits=6, decimal_places=2)
comments = models.TextField() comments = models.TextField()
def cvt_to_decimal(f):
return Decimal(f).quantize(Decimal('.01'), rounding=ROUND_HALF_DOWN)

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-30 01:22+0100\n" "POT-Creation-Date: 2024-11-30 03:54+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -264,9 +264,11 @@ msgstr "Übersicht erstellen"
msgid "Generate seminar report" msgid "Generate seminar report"
msgstr "Seminarbericht erstellen" msgstr "Seminarbericht erstellen"
#: templates/admin/members/freizeit/change_form_object_tools.html:36 #: templates/admin/members/freizeit/change_form_object_tools.html:38
msgid "Submit statement" #, fuzzy
msgstr "Abrechnung einreichen" #| msgid "Generate overview"
msgid "Finance overview"
msgstr "Übersicht erstellen"
#: templates/admin/members/member/change_form_object_tools.html:8 #: templates/admin/members/member/change_form_object_tools.html:8
msgid "Invite as user" msgid "Invite as user"
@ -286,14 +288,17 @@ msgstr "Zu Gruppe einladen"
msgid "Add another %(verbose_name)s" msgid "Add another %(verbose_name)s"
msgstr "Weiteren %(verbose_name)s hinzufügen" msgstr "Weiteren %(verbose_name)s hinzufügen"
#: utils.py:14 #: utils.py:15
msgid "Please keep filesize under {} MiB. Current filesize: {:10.2f} MiB." msgid "Please keep filesize under {} MiB. Current filesize: {:10.2f} MiB."
msgstr "Maximale Dateigröße {} MiB. Aktuelle Dateigröße: {:10.2f} MiB." msgstr "Maximale Dateigröße {} MiB. Aktuelle Dateigröße: {:10.2f} MiB."
#: utils.py:42 #: utils.py:43
msgid "Filetype not supported." msgid "Filetype not supported."
msgstr "Dateityp nicht unterstützt." msgstr "Dateityp nicht unterstützt."
#: utils.py:44 #: utils.py:45
msgid "Please keep filesize under {}. Current filesize: {}" msgid "Please keep filesize under {}. Current filesize: {}"
msgstr "Maximale Dateigröße {}. Aktuelle Dateigröße: {}." msgstr "Maximale Dateigröße {}. Aktuelle Dateigröße: {}."
#~ msgid "Submit statement"
#~ msgstr "Abrechnung einreichen"

@ -41,6 +41,7 @@ from .models import (Member, Group, Freizeit, MemberNoteList, NewMemberOnList, K
from finance.models import Statement, BillOnExcursionProxy from finance.models import Statement, BillOnExcursionProxy
from mailer.mailutils import send as send_mail, get_echo_link from mailer.mailutils import send as send_mail, get_echo_link
from django.conf import settings from django.conf import settings
from utils import get_member
#from easy_select2 import apply_select2 #from easy_select2 import apply_select2
@ -924,6 +925,25 @@ class FreizeitAdmin(CommonAdminMixin, nested_admin.NestedModelAdmin):
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, attachments)
sjr_application.short_description = _('Generate SJR application') sjr_application.short_description = _('Generate SJR application')
def finance_overview(self, request, memberlist):
if not memberlist.statement:
messages.error(request, _("No statement found. Please add a statement and then retry."))
if "apply" in request.POST:
memberlist.statement.submit(get_member(request))
messages.success(request,
_("Successfully submited statement. The finance department will notify you as soon as possible."))
return HttpResponseRedirect(reverse('admin:%s_%s_change' % (self.opts.app_label, self.opts.model_name), args=(memberlist.pk,)))
context = dict(self.admin_site.each_context(request),
title=_('Finance overview'),
opts=self.opts,
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)
def get_urls(self): def get_urls(self):
urls = super().get_urls() urls = super().get_urls()
@ -952,6 +972,8 @@ class FreizeitAdmin(CommonAdminMixin, nested_admin.NestedModelAdmin):
return self.notes_list(request, Freizeit.objects.get(pk=object_id)) return self.notes_list(request, Freizeit.objects.get(pk=object_id))
if "crisis_intervention_list" in request.POST: if "crisis_intervention_list" in request.POST:
return self.crisis_intervention_list(request, Freizeit.objects.get(pk=object_id)) return self.crisis_intervention_list(request, Freizeit.objects.get(pk=object_id))
if "finance_overview" in request.POST:
return self.finance_overview(request, Freizeit.objects.get(pk=object_id))
return HttpResponseRedirect(reverse('admin:%s_%s_change' % (self.opts.app_label, self.opts.model_name), return HttpResponseRedirect(reverse('admin:%s_%s_change' % (self.opts.app_label, self.opts.model_name),
args=(object_id,))) args=(object_id,)))

File diff suppressed because it is too large Load Diff

@ -24,6 +24,7 @@ from .rules import may_view, may_change, may_delete, is_own_training, is_oneself
import rules import rules
from contrib.models import CommonModel from contrib.models import CommonModel
from contrib.rules import memberize_user, has_global_perm from contrib.rules import memberize_user, has_global_perm
from utils import cvt_to_decimal
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
@ -1023,6 +1024,19 @@ class Freizeit(CommonModel):
jls = set(self.jugendleiter.distinct()) jls = set(self.jugendleiter.distinct())
return len(ps - jls) return len(ps - jls)
@property
def potential_ljp_contributions(self):
return cvt_to_decimal(min(25 * self.participant_count * self.duration,
0.9 * float(self.statement.total_bills_theoretic) + float(self.statement.total_staff)))
@property
def total_relative_costs(self):
if not self.statement:
return 0
total_costs = self.statement.total_bills_theoretic
total_contributions = self.statement.total_staff + self.potential_ljp_contributions
return total_costs - total_contributions
@property @property
def time_period_str(self): def time_period_str(self):
time_period = self.date.strftime('%d.%m.%Y') time_period = self.date.strftime('%d.%m.%Y')

@ -0,0 +1,166 @@
{% 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 'Finance overview' %}
</div>
{% endblock %}
{% block content %}
<h2>{% trans 'Excursion' %}: {{ memberlist.name }}</h2>
<p>
{% blocktrans %}
Here you see an estimate on the expected costs and contributions by the association. This is not a guaranteed
cost plan!
{% endblocktrans %}
</p>
<h3>{% translate "Expenses" %}</h3>
{% blocktrans %}You listed the following expenses:{% endblocktrans %}
<p>
<table>
<th>
<td>{% trans "Amount" %}</td>
</th>
{% for bill in memberlist.statement.bill_set.all %}
<tr>
<td>
{{bill.short_description}}
</td>
<td>
{{bill.explanation}}
</td>
<td>
{{ bill.amount }}€.
</td>
</tr>
{% endfor %}
</table>
</p>
<p>{% blocktrans %}The total expected expenses are {{ total_bills_theoretic }} €.{% endblocktrans %}</p>
<h3>{% trans "Contributions by the association" %}</h3>
<p>
{% blocktrans %}According to the contribution guidelines,
{{ staff_count }} youth leader(s) receive contributions. Each of them receives{% endblocktrans %}
</p>
<p>
<ul>
<li>
{% blocktrans %}{{ nights }} nights for {{ price_per_night }}€ per night making a total of {{ nights_per_yl }}€.{% endblocktrans %}
</li>
<li>
{% blocktrans %}{{ duration }} days for {{ allowance_per_day }}€ per day making a total of {{ allowance_per_yl }}€.{% endblocktrans %}
</li>
<li>
{% blocktrans %}{{ kilometers_traveled }} km by {{ means_of_transport }} ({{euro_per_km}} € / km) making a total of {{ transportation_per_yl }}€.{% endblocktrans %}
</li>
</ul>
</p>
<p>
{% blocktrans %}In total these are contributions of {{ total_per_yl }}€ times {{ staff_count }}, giving {{ total_staff }}€.{% endblocktrans %}
</p>
<h3>{% trans "LJP contributions" %}</h3>
<p>
{% blocktrans %}By submitting a seminar report, you may apply for LJP contributions. In this case,
you may obtain up to 25€ times {{ duration }} days for {{ participant_count }} participants but only up to
90% of the total costs. This results in a total of {{ ljp_contributions }}€.{% endblocktrans %}
</p>
<h3>{% trans "Summary" %}</h3>
<p>
{% blocktrans %}This is the estimated cost and contribution summary:{% endblocktrans %}
</p>
<table>
<tr>
<td>
{% trans "Expenses" %}
</td>
<td>
{{ total_bills_theoretic }}€
</td>
</tr>
<tr>
<td>
{% trans "Contributions by the association" %}
</td>
<td>
-{{ total_staff }}€
</td>
</tr>
<tr>
<td>
{% trans "Potential LJP contributions" %}
</td>
<td>
-{{ ljp_contributions }}€
</td>
</tr>
<tr>
<td>
{% trans "Remaining costs" %}
</td>
<td>
{{ total_relative_costs }}€
</td>
</tr>
</table>
<br>
<p>
{% blocktrans %}Positive remaining costs indicate that the estimated costs exceed the estimated contributions, while negative
remaining costs indicate that the estimated contributions exceed the estimated costs.{% endblocktrans %}
</p>
<p>
{% blocktrans %}Note that this cost calculation expects you to apply for LJP contributions. On the
excursions main page, you can generate a template for a seminar report.{% endblocktrans %}
</p>
{% if not memberlist.statement.submitted %}
<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,
no further changes to the statement are possible.{% endblocktrans %}
</p>
<form action="" method="post">
{% csrf_token %}
<input type="hidden" name="action" value="finance_overview">
<input type="hidden" name="finance_overview">
<input class="default" style="color: $default-link-color" type="submit" name="apply" value="{% translate 'Submit' %}">
<a href="#" class="button cancel-link">{% translate "Cancel" %}</a>
</form>
{% else %}
<br>
<h3>{% trans "Statement submitted" %}</h3>
<p>
{% blocktrans %}The statement for this excursion was already submitted. The finance department is currently processing your
data and you will receive a response shortly.{% endblocktrans %}
</p>
<a href="#" class="button cancel-link">{% translate "Back" %}</a>
{% endif %}
{% endblock %}

@ -31,9 +31,12 @@
</form> </form>
</li> </li>
{% if original.statement and not original.statement.submitted %} {% if original.statement %}
<li> <li>
<a class="historylink" href="{% url 'admin:finance_statementunsubmitted_submit' original.statement.pk %}">{% trans 'Submit statement' %}</a> <form method="post" action="{% url 'admin:members_freizeit_action' original.pk %}">
{% csrf_token %}
<input type="submit" name="finance_overview" value="{% trans 'Finance overview' %}">
</form>
</li> </li>
{% endif %} {% endif %}
{{block.super}} {{block.super}}

@ -1,6 +1,7 @@
from django.db import models from django.db import models
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from decimal import Decimal, ROUND_HALF_DOWN
def file_size_validator(max_upload_size): def file_size_validator(max_upload_size):
@ -48,3 +49,14 @@ class RestrictedFileField(models.FileField):
except AttributeError as e: except AttributeError as e:
print(e) print(e)
return data return data
def cvt_to_decimal(f):
return Decimal(f).quantize(Decimal('.01'), rounding=ROUND_HALF_DOWN)
def get_member(request):
if not hasattr(request.user, 'member'):
return None
else:
return request.user.member

Loading…
Cancel
Save