4b802ba0be
Each parent projection now carries one entry per calendar year from birth to the fourth birthday: vacation earned, carried in, planned, left, carried out and expiring under Semesterlagen's 20-day floor and 5-year carry-over, plus parental-leave days used and left at year end with the age-4 clamp applied in the birthday year. The overview sums them into per-year totals. Frontend types, mapper and fixtures follow. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XBRLomdLK9jF7wU7a8AA6H
95 lines
3.1 KiB
Python
95 lines
3.1 KiB
Python
"""API-level tests for `GET /api/analytics/{workspace_id}/overview` and the
|
|
|
|
per-parent age4 endpoint — specifically that the new per-year balance series
|
|
(`year_balances`/`year_totals`) survives the wire boundary (§4.2's
|
|
service-computed-but-dropped-on-the-wire gotcha), matching the
|
|
`test_calendar_router.py` pattern.
|
|
"""
|
|
|
|
from datetime import date, datetime
|
|
from uuid import uuid4
|
|
|
|
from httpx import AsyncClient
|
|
|
|
from app.entities.child import Child
|
|
from app.repositories_impl.sqlite.child_repository import SQLiteChildRepository
|
|
from tests.conftest import SeededIds
|
|
|
|
|
|
class TestGetOverviewYearBalances:
|
|
async def test_wire_shape_includes_year_balances_and_year_totals(
|
|
self,
|
|
authed_client: AsyncClient,
|
|
child_repo: SQLiteChildRepository,
|
|
seeded_ids: SeededIds,
|
|
) -> None:
|
|
ws = seeded_ids.workspace_id
|
|
pd_uuid = seeded_ids.personal_data_uuid
|
|
birth = date(2025, 1, 1)
|
|
child = await child_repo.create(
|
|
Child(
|
|
id=uuid4(),
|
|
workspace_id=ws,
|
|
name="Kid",
|
|
birth_date=birth,
|
|
created_at=datetime.now(),
|
|
)
|
|
)
|
|
|
|
response = await authed_client.get(
|
|
f"/api/analytics/{ws}/overview", params={"child_id": str(child.id)}
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
body = response.json()
|
|
|
|
assert "year_totals" in body
|
|
assert [yt["year"] for yt in body["year_totals"]] == [2025, 2026, 2027, 2028, 2029]
|
|
for yt in body["year_totals"]:
|
|
assert set(yt) == {"year", "pl_left", "vacation_left"}
|
|
|
|
assert len(body["parent_projections"]) == 1
|
|
projection = body["parent_projections"][0]
|
|
assert projection["personal_data_uuid"] == str(pd_uuid)
|
|
assert "year_balances" in projection
|
|
assert [yb["year"] for yb in projection["year_balances"]] == [
|
|
2025,
|
|
2026,
|
|
2027,
|
|
2028,
|
|
2029,
|
|
]
|
|
|
|
birth_year = projection["year_balances"][0]
|
|
assert birth_year["personal_data_uuid"] == str(pd_uuid)
|
|
assert birth_year["child_id"] == str(child.id)
|
|
|
|
vacation = birth_year["vacation"]
|
|
assert set(vacation) == {
|
|
"year",
|
|
"earned",
|
|
"is_assumed",
|
|
"carried_in",
|
|
"planned_use",
|
|
"left",
|
|
"carried_out",
|
|
"expiring",
|
|
}
|
|
assert vacation["earned"] == 25 # statutory default — no vacation_days row entered
|
|
assert vacation["is_assumed"] is True
|
|
assert vacation["carried_in"] == 0
|
|
assert vacation["planned_use"] == 0
|
|
assert vacation["left"] == 25
|
|
|
|
parental_leave = birth_year["parental_leave"]
|
|
assert set(parental_leave) == {
|
|
"year",
|
|
"sjukpenning_used",
|
|
"lagsta_used",
|
|
"sjukpenning_left",
|
|
"lagsta_left",
|
|
}
|
|
# `seeded_ids`'s workspace has exactly one member -> full pool.
|
|
assert parental_leave["sjukpenning_left"] == 390
|
|
assert parental_leave["lagsta_left"] == 90
|