Ergänzung Abrechnungs-Workflow
#150
Merged
christian.merten
merged 19 commits from MK/finance_workflow into main 8 months ago
@ -0,0 +1,20 @@
|
||||
# Generated by Django 4.2.20 on 2025-04-03 21:04
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('members', '0039_membertraining_certificate_attendance'),
|
||||
('finance', '0008_alter_statement_allowance_to_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='statement',
|
||||
name='ljp_to',
|
||||
field=models.ForeignKey(blank=True, help_text='The person that should receive the ljp contributions for the participants. Should be only selected if an ljp request was submitted.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='receives_ljp_for_statements', to='members.member', verbose_name='Pay ljp contributions to'),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,137 @@
|
||||
{% extends "members/tex_base.tex" %}
|
||||
{% load static common tex_extras %}
|
||||
|
||||
{% block title %}Abrechnungs- und Zuschussbeleg\\[2mm]Sektionsveranstaltung{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if excursion %}
|
||||
\noindent\textbf{\large Ausfahrt}
|
||||
|
||||
% DESCRIPTION TABLE
|
||||
\begin{table}[H]
|
||||
\begin{tabular}{ll}
|
||||
Aktivität: & {{ excursion.name|esc_all }} \\
|
||||
Ordnungsnummer & {{ excursion.code|esc_all }} \\
|
||||
Ort / Stützpunkt: & {{ excursion.place|esc_all }} \\
|
||||
Zeitraum: & {{ excursion.duration|esc_all}} Tage ({{ excursion.time_period_str|esc_all }}) \\
|
||||
Teilnehmer*innen: & {{ excursion.participant_count }} der Gruppe(n) {{ excursion.groups_str|esc_all }} \\
|
||||
Betreuer*innen: & {{excursion.staff_count|esc_all }} ({{ excursion.staff_str|esc_all }}) \\
|
||||
Art der Tour: & {% checked_if_true 'Gemeinschaftstour' excursion.get_tour_type %}
|
||||
{% checked_if_true 'Führungstour' excursion.get_tour_type %}
|
||||
{% checked_if_true 'Ausbildung' excursion.get_tour_type %} \\
|
||||
Anreise: & {% checked_if_true 'ÖPNV' excursion.get_tour_approach %}
|
||||
{% checked_if_true 'Muskelkraft' excursion.get_tour_approach %}
|
||||
{% checked_if_true 'Fahrgemeinschaften' excursion.get_tour_approach %}
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
\noindent\textbf{\large Zuschüsse und Aufwandsentschädigung}
|
||||
{% if excursion.approved_staff_count > 0 %}
|
||||
|
||||
\noindent Gemäß Beschluss des Jugendausschusses gelten folgende Sätze für Zuschüsse pro genehmigter Jugendleiter*in:
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{.97\textwidth}{Xllr}
|
||||
\toprule
|
||||
\textbf{Posten} & \textbf{Einzelsatz} & \textbf{Anzahl} & \textbf{Gesamtbetrag pro JL} \\
|
||||
\midrule
|
||||
Zuschuss Übernachtung & {{ statement.price_per_night }} € / Nacht & {{ statement.nights }} Nächte & {{ statement.nights_per_yl }} € \\
|
||||
Zuschuss Anreise & {{statement.euro_per_km}} € / km ({{ statement.means_of_transport }}) & {{ statement.kilometers_traveled }} km & {{ statement.transportation_per_yl }} € \\
|
||||
Aufwandsentschädigung & {{ statement.allowance_per_day }},00 € / Tag & {{ statement.duration }} Tage & {{ statement.allowance_per_yl }} € \\
|
||||
\midrule
|
||||
\textbf{Summe}& & & \textbf{ {{ statement.total_per_yl }} }€\\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\end{table}
|
||||
|
||||
\noindent Gemäß JDAV-Betreuungsschlüssel können bei {{ excursion.participant_count }} Teilnehmer*innen
|
||||
bis zu {{ excursion.approved_staff_count }} Jugendleiter*innen {% if excursion.approved_extra_youth_leader_count %}
|
||||
(davon {{ excursion.approved_extra_youth_leader_count }} durch das Jugendreferat zusätzlich genehmigt){% endif %} bezuschusst werden.
|
||||
Zuschüsse und Aufwandsentschädigung werden wie folgt abgerufen:
|
||||
\begin{itemize}
|
||||
|
||||
{% if statement.allowances_paid > 0 %}
|
||||
|
||||
\item Eine Aufwandsentschädigung von {{ statement.allowance_per_yl }} € pro Jugendleiter*in wird überwiesen an:
|
||||
{% for m in statement.allowance_to.all %}{% if forloop.counter > 1 %}, {% endif %}{{ m.name }}{% endfor %}
|
||||
{% else %}
|
||||
\item Keiner*r der Jugendleiter*innen nimmt eine Aufwandsentschädigung in Anspruch.
|
||||
{% endif %}
|
||||
|
||||
{% if statement.subsidy_to %}
|
||||
\item Der Zuschuss zu Übernachtung und Anreise für alle Jugendleiter*innen in Höhe von {{ statement.total_subsidies }} € wird überwiesen an:
|
||||
{{ statement.subsidy_to.name }}
|
||||
|
||||
{% else %}
|
||||
\item Zuschüsse zu Übernachtung und Anreise werden nicht in Anspruch genommen.
|
||||
{% endif %}
|
||||
|
||||
\end{itemize}
|
||||
{% else %}
|
||||
\noindent Für die vorliegende Ausfahrt sind keine Jugendleiter*innen anspruchsberechtigt für Zuschüsse oder Aufwandsentschädigung.
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if statement.ljp_to %}
|
||||
\noindent\textbf{LJP-Zuschüsse}
|
||||
|
||||
\noindent Der LJP-Zuschuss für die Teilnehmenden in Höhe von {{ statement.paid_ljp_contributions|esc_all }} € wird überwiesen an:
|
||||
{{ statement.ljp_to.name|esc_all }} Dieser Zuschuss wird aus Landesmitteln gewährt und ist daher
|
||||
in der Ausgabenübersicht gesondert aufgeführt.
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
\vspace{110pt}
|
||||
{% endif %}
|
||||
|
||||
|
||||
\vspace{12pt}
|
||||
|
||||
\noindent\textbf{\large Ausgabenübersicht}
|
||||
\nopagebreak
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{.97\textwidth}{lXlr}
|
||||
\toprule
|
||||
\textbf{Titel} & \textbf{Beschreibung} & \textbf{Auszahlung an} & \textbf{Betrag} \\
|
||||
\midrule
|
||||
|
||||
{% if statement.bills_covered %}
|
||||
{% for bill in statement.bills_covered %}
|
||||
{{ forloop.counter }}. {{ bill.short_description}} & {{ bill.explanation}} & {{ bill.paid_by.name|esc_all }} & {{ bill.amount }} € \\
|
||||
{% endfor %}
|
||||
\midrule
|
||||
\multicolumn{3}{l}{\textbf{Summe übernommene Ausgaben}} & \textbf{ {{ statement.total_bills }} }€\\
|
||||
{% endif %}
|
||||
{% if excursion.approved_staff_count > 0 and statement.allowances_paid > 0 or excursion.approved_staff_count > 0 and statement.subsidy_to %}
|
||||
\midrule
|
||||
{% if statement.allowances_paid > 0 %}
|
||||
{% for m in statement.allowance_to.all %}
|
||||
Aufwandsentschädigung & & {{ m.name|esc_all }} & {{ statement.allowance_per_yl }} €\\
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if statement.subsidy_to %}
|
||||
\multicolumn{2}{l}{Zuschuss Übernachtung und Anreise für alle Jugendleiter*innen} & {{ statement.subsidy_to.name|esc_all }} & {{ statement.total_subsidies }} €\\
|
||||
{% endif %}
|
||||
\midrule
|
||||
\multicolumn{3}{l}{\textbf{Summe Zuschüsse und Aufwandsentschädigung Jugendleitende}} & \textbf{ {{ statement.total_staff }} }€\\
|
||||
{%endif %}
|
||||
{% if statement.ljp_to %}
|
||||
\midrule
|
||||
LJP-Zuschuss für die Teilnehmenden && {{ statement.ljp_to.name|esc_all }} & {{ statement.paid_ljp_contributions|esc_all }} €\\
|
||||
|
||||
{% endif %}
|
||||
{% if statement.ljp_to or statement.bills_covered and excursion.approved_staff_count > 0 %}
|
||||
\midrule
|
||||
\textbf{Gesamtsumme}& & & \textbf{ {{ statement.total }} }€\\
|
||||
{% endif %}
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\end{table}
|
||||
|
||||
\noindent Dieser Beleg wird automatisch erstellt und daher nicht unterschrieben.
|
||||
|
||||
{% endblock %}
|
||||
@ -8,6 +8,7 @@ import csv
|
|||||||
from django.db import models
|
|||||||
from django.db.models import TextField, ManyToManyField, ForeignKey, Count,\
|
|||||||
Sum, Case, Q, F, When, Value, IntegerField, Subquery, OuterRef
|
|||||||
from django.db.models.functions import TruncDate
|
|||||||
from django.utils.translation import gettext_lazy as _
|
|||||||
from django.utils import timezone
|
|||||||
from django.utils.html import format_html
|
|||||||
@ -1278,6 +1279,30 @@ class Freizeit(CommonModel):
|
|||||||
else:
|
|||||||
return 0
|
|||||||
|
|||||||
@property
|
|||||||
|
christian.merten marked this conversation as resolved
christian.merten
commented 8 months ago
Review
Das bedeutet wir erzwingen jetzt, dass LJP Anträge im Kompass erstellt werden, richtig? (Finde ich gut, ich meine mich nur zu erinnern, dass du das mal anders gesehen hast) Das bedeutet wir erzwingen jetzt, dass LJP Anträge im Kompass erstellt werden, richtig? (Finde ich gut, ich meine mich nur zu erinnern, dass du das mal anders gesehen hast)
|
|||||||
def total_seminar_days(self):
|
|||||||
"""calculate seminar days based on intervention hours in every day"""
|
|||||||
# TODO: add tests for this
|
|||||||
if hasattr(self, 'ljpproposal'):
|
|||||||
hours_per_day = (
|
|||||||
self.ljpproposal.intervention_set
|
|||||||
.annotate(day=TruncDate('date_start')) # Extract the date (without time)
|
|||||||
.values('day') # Group by day
|
|||||||
.annotate(total_duration=Sum('duration')) # Sum durations for each day
|
|||||||
.order_by('day') # Sort results by date
|
|||||||
)
|
|||||||
# Calculate the total number of seminar days
|
|||||||
# Each day is counted as 1 if total_duration is >= 5 hours, as 0.5 if total_duration is >= 2.5
|
|||||||
# otherwise 0
|
|||||||
return sum([min(math.floor(h['total_duration']/cvt_to_decimal(2.5))/2, 1) for h in hours_per_day])
|
|||||||
else:
|
|||||||
return 0
|
|||||||
|
|||||||
@property
|
|||||||
def ljp_duration(self):
|
|||||||
"""calculate the duration in days for the LJP"""
|
|||||||
return min(self.duration, self.total_seminar_days)
|
|||||||
|
|||||||
@property
|
|||||||
def staff_count(self):
|
|||||||
return self.jugendleiter.count()
|
|||||||
@ -1366,12 +1391,19 @@ class Freizeit(CommonModel):
|
|||||||
return cvt_to_decimal(min(self.maximal_ljp_contributions,
|
|||||||
0.9 * float(self.statement.total_bills_theoretic) + float(self.statement.total_staff)))
|
|||||||
|
|||||||
@property
|
|||||||
def payable_ljp_contributions(self):
|
|||||||
"""from the requested ljp contributions, a tax may be deducted for risk reduction"""
|
|||||||
if self.statement.ljp_to:
|
|||||||
return self.statement.paid_ljp_contributions
|
|||||||
return cvt_to_decimal(self.potential_ljp_contributions * cvt_to_decimal(1 - settings.LJP_TAX))
|
|||||||
|
|||||||
@property
|
|||||||
def total_relative_costs(self):
|
|||||||
if not self.statement:
|
|||||||
return 0
|
|||||||
total_costs = self.statement.total_bills_theoretic
|
|||||||
total_contributions = self.statement.total_subsidies + self.potential_ljp_contributions
|
|||||||
total_contributions = self.statement.total_subsidies + self.payable_ljp_contributions
|
|||||||
return total_costs - total_contributions
|
|||||||
|
|||||||
@property
|
|||||||
|
|||||||
Loading…
Reference in New Issue
Ich denke es sollte
0.9 * ( float(self.total_bills_theoretic) + float(self.total_staff))sein. Der LJP macht keinen Unterschied zwischenself.total_bills_theoreticundself.total_staff.