fix(absences): POST /absences/ reicht warnings jetzt an den Client durch
create_absence() gibt (absence, warnings) zurück, der Router verwarf die Warnungen bisher (im Unterschied zu approve_absence, das sie via AbsenceApproveOut.warnings durchreicht). Neues AbsenceCreateOut-Schema analog dazu. Regressionstest über Überschneidungs-Warnung. Gefunden während Coverage-Arbeit an absence_service.py. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Ahyx6D3r7G1EuAc42nezn
This commit is contained in:
@@ -262,7 +262,11 @@ async def list_absences(
|
||||
return AbsenceListResponse(total=total, items=[AbsenceOut.model_validate(a) for a in absences])
|
||||
|
||||
|
||||
@router.post("/absences/", response_model=AbsenceOut, status_code=201)
|
||||
class AbsenceCreateOut(AbsenceOut):
|
||||
warnings: list[str] = []
|
||||
|
||||
|
||||
@router.post("/absences/", response_model=AbsenceCreateOut, status_code=201)
|
||||
async def create_absence(
|
||||
data: AbsenceCreate,
|
||||
current_user: CurrentUser,
|
||||
@@ -281,7 +285,9 @@ async def create_absence(
|
||||
acting_user = target
|
||||
absence, warnings = await absence_service.create_absence(data, acting_user, db)
|
||||
await db.commit()
|
||||
return AbsenceOut.model_validate(absence)
|
||||
out = AbsenceCreateOut.model_validate(absence)
|
||||
out.warnings = warnings
|
||||
return out
|
||||
|
||||
|
||||
@router.patch("/absences/{absence_id}", response_model=AbsenceOut)
|
||||
|
||||
@@ -8,6 +8,13 @@ Deckt:
|
||||
+ Schwellwert-Grenzfall working_days == two_stage_min_days
|
||||
5. DATEV-Export: Feiertag-Kürzel "F" schlägt Urlaubs-Kürzel "U"; working_days konsistent
|
||||
6. _categorize_hours: Nachtschicht über Mitternacht + Sonntag+Feiertag (höchster Zuschlag gewinnt)
|
||||
7. POST /absences/ reicht warnings jetzt durch (API-Konsistenz-Fix)
|
||||
|
||||
Nebenbei gefixte Bugs (während dieser Coverage-Arbeit gefunden):
|
||||
- _get_holiday_dates() rechnete Feiertage im Folgejahr bei Anträgen über den
|
||||
Jahreswechsel nicht aus working_days heraus (siehe
|
||||
test_holiday_dates_cover_year_boundary + absence_service.py year_to-Parameter)
|
||||
- POST /absences/ verwarf die warnings-Liste von create_absence() (siehe Test 7)
|
||||
"""
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
@@ -116,12 +123,9 @@ async def test_carryover_expiry_via_create_absence(
|
||||
nicht das Signal 'Konto reicht' bekommen, obwohl entitled+carried_over rechnerisch
|
||||
reichen würde – der verfallene Übertrag darf effective_available nicht aufblähen.
|
||||
|
||||
Ruft absence_service.create_absence() direkt auf (statt über HTTP), weil der
|
||||
Router POST /absences/ die von create_absence() zurückgegebenen `warnings`
|
||||
**verwirft** (siehe app/routers/absences.py::create_absence – nur approve_absence
|
||||
reicht `warnings` über AbsenceOut.warnings durch). Das ist ein eigenständiger,
|
||||
hier entdeckter Bug (siehe Abschlussbericht), separat vom hier geprüften
|
||||
Verfalls-Verhalten von effective_available()."""
|
||||
Ruft absence_service.create_absence() direkt auf (statt über HTTP) um den
|
||||
warnings-Rückgabewert isoliert vom HTTP-Layer zu prüfen (Test 7 unten deckt
|
||||
den HTTP-Pfad inkl. warnings-Durchreichung separat ab)."""
|
||||
from app.services.absence_service import absence_service
|
||||
from app.schemas.absence import AbsenceCreate
|
||||
|
||||
@@ -580,3 +584,31 @@ def test_categorize_hours_sunday_and_holiday_highest_wins():
|
||||
assert result.holiday_150_hours == pytest.approx(8.0)
|
||||
assert result.sunday_hours == pytest.approx(0.0)
|
||||
assert result.holiday_125_hours == pytest.approx(0.0)
|
||||
|
||||
|
||||
# ── 7. POST /absences/ gibt warnings zurück (API-Konsistenz-Fix) ────────────────
|
||||
|
||||
async def test_create_absence_returns_warnings(
|
||||
client: AsyncClient, cov_headers, cov_vacation_type_id,
|
||||
):
|
||||
"""Regressionstest: POST /absences/ verwarf bisher die von create_absence()
|
||||
zurückgegebenen warnings (im Unterschied zu approve_absence, das sie über
|
||||
AbsenceApproveOut.warnings durchreicht). Fix: neues AbsenceCreateOut-Schema.
|
||||
Trigger hier: Überschneidungs-Warnung bei zwei sich überlappenden Anträgen."""
|
||||
start = _future_monday(20)
|
||||
end = start + timedelta(days=2)
|
||||
|
||||
resp1 = await client.post("/api/v1/absences/", json={
|
||||
"type_id": str(cov_vacation_type_id), "start_date": str(start), "end_date": str(end),
|
||||
}, headers=cov_headers)
|
||||
assert resp1.status_code == 201, resp1.text
|
||||
assert resp1.json().get("warnings") == []
|
||||
|
||||
# Überlappender zweiter Antrag im selben Zeitraum -> Warnung erwartet
|
||||
resp2 = await client.post("/api/v1/absences/", json={
|
||||
"type_id": str(cov_vacation_type_id), "start_date": str(start), "end_date": str(end + timedelta(days=1)),
|
||||
}, headers=cov_headers)
|
||||
assert resp2.status_code == 201, resp2.text
|
||||
data2 = resp2.json()
|
||||
assert "warnings" in data2
|
||||
assert any("Überschneidung" in w for w in data2["warnings"]), data2["warnings"]
|
||||
|
||||
Reference in New Issue
Block a user