"""Tests for src/pdf_filler.py — field dict building and pauschale/plan logic.""" from datetime import date import pytest from src.models import AttendanceDate, MonthlyAttendance, Person from src.pdf_filler import build_field_dict # --------------------------------------------------------------------------- # Fixtures # --------------------------------------------------------------------------- @pytest.fixture def cfg(): return { "antragsnummer": "SAB-12345", "ort": "Leipzig", "template_pdf": "template/sab.pdf", "output_dir": "output", "plan_hours_value": "6", "pdf_field_ort": "unterschrift.ort", "pdf_field_erstellungsdatum": "unterschrift.datum", "pdf_pauschale_prefix": "pauschale_5", "pauschale_present_value": "1", "excel_year_pattern": "(20\\d{2})", "excel_name_header": "Name, Vorname", "excel_name_column": 1, "excel_seq_column": 0, "excel_day_start_col": 9, "excel_day_end_col": 39, "month_mapping": {"Feb": "02"}, "valid_status_codes": ["K", "KK", "E", "F", "P", "U"], } def _make_attendance(*entries) -> MonthlyAttendance: person = Person(seq_num=1, name="Teilnehmer, Test") return MonthlyAttendance( person=person, year=2026, month=2, month_name="Feb", entries=list(entries), ) def _present(day: int, hours: float) -> AttendanceDate: return AttendanceDate(date=date(2026, 2, day), status="X", hours=hours) def _absent(day: int, status: str) -> AttendanceDate: return AttendanceDate(date=date(2026, 2, day), status=status, hours=None) # --------------------------------------------------------------------------- # Header fields # --------------------------------------------------------------------------- class TestHeaderFields: def test_person_name(self, cfg): fields = build_field_dict(_make_attendance(), cfg, date(2026, 2, 1)) assert fields["person.name"] == "Teilnehmer, Test" def test_monat_jahr(self, cfg): fields = build_field_dict(_make_attendance(), cfg, date(2026, 2, 1)) assert fields["monat_jahr"] == "02/2026" def test_antragsnummer(self, cfg): fields = build_field_dict(_make_attendance(), cfg, date(2026, 2, 1)) assert fields["antragsnummer"] == "SAB-12345" def test_ort(self, cfg): fields = build_field_dict(_make_attendance(), cfg, date(2026, 2, 1)) assert fields["unterschrift.ort"] == "Leipzig" def test_erstellungsdatum(self, cfg): fields = build_field_dict(_make_attendance(), cfg, date(2026, 3, 27)) assert fields["unterschrift.datum"] == "27.03.2026" # --------------------------------------------------------------------------- # Present day (status X) # --------------------------------------------------------------------------- class TestPresentDay: def test_datum_field(self, cfg): fields = build_field_dict(_make_attendance(_present(3, 6.0)), cfg, date(2026, 2, 1)) assert fields["datum.0"] == "03.02.2026" def test_anwesend_field(self, cfg): fields = build_field_dict(_make_attendance(_present(3, 6.0)), cfg, date(2026, 2, 1)) assert fields["anwesend.0"] == "X" def test_ist_field_whole_hours(self, cfg): fields = build_field_dict(_make_attendance(_present(3, 6.0)), cfg, date(2026, 2, 1)) assert fields["ist.0"] == "6" def test_ist_field_decimal_hours(self, cfg): fields = build_field_dict(_make_attendance(_present(3, 6.5)), cfg, date(2026, 2, 1)) assert fields["ist.0"] == "6,5" def test_plan_always_6(self, cfg): fields = build_field_dict(_make_attendance(_present(3, 3.0)), cfg, date(2026, 2, 1)) assert fields["plan.0"] == "6" def test_pauschale_set(self, cfg): fields = build_field_dict(_make_attendance(_present(3, 6.0)), cfg, date(2026, 2, 1)) assert fields["pauschale_5.0"] == "1" # --------------------------------------------------------------------------- # Absent days (K, KK, E, F, P, U) # --------------------------------------------------------------------------- class TestAbsentDay: @pytest.mark.parametrize("status", ["K", "KK", "E", "F", "P", "U"]) def test_anwesend_field(self, cfg, status): fields = build_field_dict(_make_attendance(_absent(4, status)), cfg, date(2026, 2, 1)) assert fields["anwesend.0"] == status @pytest.mark.parametrize("status", ["K", "KK", "E", "F", "P", "U"]) def test_ist_empty(self, cfg, status): fields = build_field_dict(_make_attendance(_absent(4, status)), cfg, date(2026, 2, 1)) assert fields["ist.0"] == "" @pytest.mark.parametrize("status", ["K", "KK", "E", "F", "P", "U"]) def test_plan_empty(self, cfg, status): fields = build_field_dict(_make_attendance(_absent(4, status)), cfg, date(2026, 2, 1)) assert fields["plan.0"] == "" @pytest.mark.parametrize("status", ["K", "KK", "E", "F", "P", "U"]) def test_pauschale_empty(self, cfg, status): fields = build_field_dict(_make_attendance(_absent(4, status)), cfg, date(2026, 2, 1)) assert fields["pauschale_5.0"] == "0" # --------------------------------------------------------------------------- # Multi-row indexing # --------------------------------------------------------------------------- class TestMultiRow: def test_second_entry_indexed_as_1(self, cfg): entries = [_present(1, 6.0), _absent(2, "K")] fields = build_field_dict(_make_attendance(*entries), cfg, date(2026, 2, 1)) assert fields["datum.1"] == "02.02.2026" assert fields["anwesend.1"] == "K" def test_23_entries_produce_index_0_to_22(self, cfg): entries = [_present(i, 6.0) for i in range(1, 24)] # 23 entries max fields = build_field_dict(_make_attendance(*entries), cfg, date(2026, 2, 1)) # Index 22 should exist assert "datum.22" in fields assert "anwesend.22" in fields # Index 23 must NOT exist assert "datum.23" not in fields # --------------------------------------------------------------------------- # Hours formatting # --------------------------------------------------------------------------- class TestHoursFormatting: def test_whole_number_no_decimal(self): entry = AttendanceDate(date=date(2026, 2, 1), status="X", hours=6.0) assert entry.hours_german() == "6" def test_decimal_comma_separator(self): entry = AttendanceDate(date=date(2026, 2, 1), status="X", hours=6.5) assert entry.hours_german() == "6,5" def test_none_returns_empty(self): entry = AttendanceDate(date=date(2026, 2, 1), status="K", hours=None) assert entry.hours_german() == ""