Files
bin/finance/tests/test_double_count.py
T
2026-09-29 07:27:30 +02:00

100 lines
4.2 KiB
Python

from datetime import date
from decimal import Decimal
import pytest
from app.models.tables import Loan, RecurringItem
from app.services.double_count import (KreditDaten, PostenDaten, all_conflicts,
conflicts_for_loan, conflicts_for_recurring,
find_conflict, rate_period, regular_rate)
D = Decimal
# 1.200 € zu 0 % auf 12 Monate, Auszahlung 15.01.2026 -> Rate 100,00;
# erste Rate 15.02.2026, letzte 15.01.2027.
K100 = KreditDaten(1, "Auto", D("1200.00"), D("0"), 12, date(2026, 1, 15), "annuity")
def P(amount, rhythm="monthly", start=None, end=None, name="Rate"):
return PostenDaten(7, name, D(amount), rhythm, start, end)
def test_regular_rate_und_zeitraum():
assert regular_rate(K100) == D("100.00")
assert rate_period(K100) == (date(2026, 2, 15), date(2027, 1, 15))
annu = KreditDaten(2, "A", D("5000.00"), D("6.0"), 48, date(2026, 1, 1), "annuity")
assert regular_rate(annu) == D("117.43")
bullet = KreditDaten(3, "B", D("12000.00"), D("6.0"), 24, date(2026, 1, 1), "bullet")
assert regular_rate(bullet) == D("60.00")
bullet0 = KreditDaten(4, "B0", D("12000.00"), D("0"), 24, date(2026, 1, 1), "bullet")
assert regular_rate(bullet0) is None
@pytest.mark.parametrize("amount,hit", [
("-100.00", True), ("-103.00", True), ("-103.01", False),
("-97.00", True), ("-96.99", False),
])
def test_toleranz_drei_prozent(amount, hit):
assert (find_conflict(P(amount), K100) is not None) is hit
@pytest.mark.parametrize("amount,hit", [("-21.00", True), ("-21.01", False), ("-19.00", True)])
def test_mindesttoleranz_ein_euro(amount, hit):
k20 = KreditDaten(5, "Klein", D("240.00"), D("0"), 12, date(2026, 1, 15), "annuity")
assert (find_conflict(P(amount), k20) is not None) is hit
def test_endfaellig_zinsrate_und_nullzins():
bullet = KreditDaten(3, "B", D("12000.00"), D("6.0"), 24, date(2026, 1, 1), "bullet")
assert find_conflict(P("-60.00"), bullet) is not None
bullet0 = KreditDaten(4, "B0", D("12000.00"), D("0"), 24, date(2026, 1, 1), "bullet")
assert find_conflict(P("-60.00"), bullet0) is None
assert find_conflict(P("-0.50"), bullet0) is None
def test_nur_monatliche_ausgaben():
assert find_conflict(P("-100.00", rhythm="quarterly"), K100) is None
assert find_conflict(P("100.00"), K100) is None
@pytest.mark.parametrize("start,end,hit", [
(None, date(2026, 2, 14), False), (None, date(2026, 2, 15), True),
(date(2027, 1, 16), None, False), (date(2027, 1, 15), None, True),
(None, None, True), (date(2025, 1, 1), date(2030, 1, 1), True),
])
def test_zeitraeume(start, end, hit):
assert (find_conflict(P("-100.00", start=start, end=end), K100) is not None) is hit
def test_konflikt_inhalt():
k = find_conflict(P("-101.50", name="Rate Auto"), K100)
assert (k.posten_id, k.posten_name, k.posten_betrag) == (7, "Rate Auto", D("-101.50"))
assert (k.kredit_id, k.kredit_name, k.kredit_rate) == (1, "Auto", D("100.00"))
assert k.paar == (7, 1)
def _seed(db):
db.add_all([
Loan(name="Auto", principal=D("1200.00"), annual_rate_pct=D("0"), term_months=12,
payout_date=date(2026, 1, 15), repayment_type="annuity"),
Loan(name="Möbel", principal=D("240.00"), annual_rate_pct=D("0"), term_months=12,
payout_date=date(2026, 1, 15), repayment_type="annuity"),
RecurringItem(name="Rate Auto", amount=D("-100.00"), rhythm="monthly", due_day=15),
RecurringItem(name="Rate Möbel", amount=D("-20.00"), rhythm="monthly", due_day=15),
RecurringItem(name="Miete", amount=D("-600.00"), rhythm="monthly", due_day=1),
])
db.commit()
def test_all_conflicts_findet_paare_sortiert(db):
_seed(db)
assert [(k.posten_name, k.kredit_name) for k in all_conflicts(db)] == [
("Rate Auto", "Auto"), ("Rate Möbel", "Möbel")]
def test_conflicts_for_recurring_und_loan(db):
_seed(db)
neu = PostenDaten(None, "Neu", D("-99.00"), "monthly", None, None)
assert [k.kredit_name for k in conflicts_for_recurring(db, neu)] == ["Auto"]
kredit = KreditDaten(None, "Neu", D("7200.00"), D("0"), 12, date(2026, 1, 1), "annuity")
assert [k.posten_name for k in conflicts_for_loan(db, kredit)] == ["Miete"]