feat (excursion): added tests and documentation

pull/155/head
mariusrklein 7 months ago
parent d4137effa4
commit d46f30c364

@ -395,6 +395,8 @@ class Statement(CommonModel):
@property
def org_fee_payant(self):
if self.total_org_fee == 0:
return None
return self.subsidy_to if self.subsidy_to else self.allowance_to.all()[0]
@property
@ -456,8 +458,11 @@ class Statement(CommonModel):
return cvt_to_decimal(
min(
# if total costs are more than the max amount of the LJP contribution, we pay the max amount, reduced by taxes
(1-settings.LJP_TAX) * settings.LJP_CONTRIBUTION_PER_DAY * self.excursion.ljp_participant_count * self.excursion.ljp_duration,
# if the total costs are less than the max amount, we pay up to 90% of the total costs, reduced by taxes
(1-settings.LJP_TAX) * 0.9 * (float(self.total_bills_not_covered) + float(self.total_staff) ),
# we never pay more than the maximum costs of the trip
float(self.total_bills_not_covered)
)
)

@ -5,8 +5,9 @@ from django.conf import settings
from .models import Statement, StatementUnSubmitted, StatementSubmitted, Bill, Ledger, Transaction,\
StatementUnSubmittedManager, StatementSubmittedManager, StatementConfirmedManager,\
StatementConfirmed, TransactionIssue, StatementManager
from members.models import Member, Group, Freizeit, GEMEINSCHAFTS_TOUR, MUSKELKRAFT_ANREISE, NewMemberOnList,\
from members.models import Member, Group, Freizeit, LJPProposal, Intervention, GEMEINSCHAFTS_TOUR, MUSKELKRAFT_ANREISE, NewMemberOnList,\
FAHRGEMEINSCHAFT_ANREISE, MALE, FEMALE, DIVERSE
from dateutil.relativedelta import relativedelta
# Create your tests here.
class StatementTestCase(TestCase):
@ -66,6 +67,117 @@ class StatementTestCase(TestCase):
email=settings.TEST_MAIL, gender=DIVERSE)
mol = NewMemberOnList.objects.create(member=m, memberlist=ex)
ex.membersonlist.add(mol)
ex = Freizeit.objects.create(name='Wild trip with old people', kilometers_traveled=self.kilometers_traveled,
tour_type=GEMEINSCHAFTS_TOUR,
tour_approach=MUSKELKRAFT_ANREISE,
difficulty=2, date=timezone.datetime(2024, 1, 2, 8, 0, 0, tzinfo=timezone.utc), end=timezone.datetime(2024, 1, 5, 17, 0, 0, tzinfo=timezone.utc) )
settings.EXCURSION_ORG_FEE = 20
settings.LJP_TAX = 0.2
settings.LJP_CONTRIBUTION_PER_DAY = 20
self.st5 = Statement.objects.create(night_cost=self.night_cost, excursion=ex)
for i in range(9):
m = Member.objects.create(prename='Peter {}'.format(i), lastname='Walter', birth_date=timezone.now().date() - relativedelta(years=i+21),
email=settings.TEST_MAIL, gender=DIVERSE)
mol = NewMemberOnList.objects.create(member=m, memberlist=ex)
ex.membersonlist.add(mol)
ljpproposal = LJPProposal.objects.create(
title='Test proposal',
category=LJPProposal.LJP_STAFF_TRAINING,
goal=LJPProposal.LJP_ENVIRONMENT,
goal_strategy='my strategy',
not_bw_reason=LJPProposal.NOT_BW_ROOMS,
excursion=self.st5.excursion)
for i in range(3):
int = Intervention.objects.create(
date_start=timezone.datetime(2024, 1, 2+i, 12, 0, 0, tzinfo=timezone.utc),
duration = 2+i,
activity = 'hi',
ljp_proposal=ljpproposal
)
int.save()
ljpproposal.save()
self.b1 = Bill.objects.create(
statement=self.st5,
short_description='covered bill',
explanation='hi',
amount='300',
paid_by=self.fritz,
costs_covered=True,
refunded=False
)
self.b2 = Bill.objects.create(
statement=self.st5,
short_description='non-covered bill',
explanation='hi',
amount='900',
paid_by=self.fritz,
costs_covered=False,
refunded=False
)
def test_org_fee(self):
# org fee should be collected if participants are older than 26
self.assertEqual(self.st5.excursion.old_participant_count, 3, 'Calculation of number of old people in excursion is incorrect.')
total_org = 4 * 3 * 20 # 4 days, 3 old people, 20€ per day
self.assertEqual(self.st5.total_org_fee_theoretical, total_org, 'Theoretical org_fee should equal to amount per day per person * n_persons * n_days if there are old people.')
self.assertEqual(self.st5.total_org_fee, 0, 'Paid org fee should be 0 if no allowance and subsidies are paid if there are old people.')
self.assertIsNone(self.st5.org_fee_payant)
# now collect subsidies
self.st5.subsidy_to = self.fritz
self.assertEqual(self.st5.total_org_fee, total_org, 'Paid org fee should equal to amount per day per person * n_persons * n_days if subsidies are paid.')
# now collect allowances
self.st5.allowance_to.add(self.fritz)
self.st5.subsidy_to = None
self.assertEqual(self.st5.total_org_fee, total_org, 'Paid org fee should equal to amount per day per person * n_persons * n_days if allowances are paid.')
# now collect both
self.st5.subsidy_to = self.fritz
self.assertEqual(self.st5.total_org_fee, total_org, 'Paid org fee should equal to amount per day per person * n_persons * n_days if subsidies and allowances are paid.')
self.assertEqual(self.st5.org_fee_payant, self.fritz, 'Org fee payant should be the receiver allowances and subsidies.')
# return to previous state
self.st5.subsidy_to = None
self.st5.allowance_to.remove(self.fritz)
def test_ljp_payment(self):
expected_intervention_hours = 2 + 3 + 4
expected_seminar_days = 0 + 0.5 + 0.5 # >=2.5h = 0.5days, >=5h = 1.0day
expected_ljp = (1-settings.LJP_TAX) * expected_seminar_days * settings.LJP_CONTRIBUTION_PER_DAY * 9
# (1 - 20% tax) * 1 seminar day * 20€ * 9 participants
self.assertEqual(self.st5.excursion.total_intervention_hours, expected_intervention_hours, 'Calculation of total intervention hours is incorrect.')
self.assertEqual(self.st5.excursion.total_seminar_days, expected_seminar_days, 'Calculation of total seminar days is incorrect.')
self.assertEqual(self.st5.paid_ljp_contributions, 0, 'No LJP contributions should be paid if no receiver is set.')
# now we want to pay out the LJP contributions
self.st5.ljp_to = self.fritz
self.assertEqual(self.st5.paid_ljp_contributions, expected_ljp, 'LJP contributions should be paid if a receiver is set.')
# now the total costs paid by trip organisers is lower than expected ljp contributions, should be reduced automatically
self.b2.amount=100
self.b2.save()
self.assertEqual(self.st5.total_bills_not_covered, 100, 'Changes in bills should be reflected in the total costs paid by trip organisers')
self.assertGreaterEqual(self.st5.total_bills_not_covered, self.st5.paid_ljp_contributions, 'LJP contributions should be less than or equal to the costs paid by trip organisers')
self.st5.ljp_to = None
def test_staff_count(self):
self.assertEqual(self.st4.admissible_staff_count, 0,

@ -1418,16 +1418,23 @@ class Freizeit(CommonModel):
@property
def maximal_ljp_contributions(self):
"""This is the maximal amount of LJP contributions that can be requested given participants and length
This calculation if intended for the LJP application, not for the payout."""
return cvt_to_decimal(settings.LJP_CONTRIBUTION_PER_DAY * self.ljp_participant_count * self.duration)
@property
def potential_ljp_contributions(self):
"""The maximal amount can be reduced if the actual costs are lower than the maximal amount
This calculation if intended for the LJP application, not for the payout."""
if not self.statement:
return cvt_to_decimal(0)
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"""
"""the payable contributions can differ from potential contributions if a tax is deducted for risk reduction.
the actual payout depends on more factors, e.g. the actual costs that had to be paid by the trip organisers."""
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))

Loading…
Cancel
Save