Scadenzario aggiunta pratiche

This commit is contained in:
2026-06-17 22:09:18 +02:00
parent 3fd3c72f06
commit 64442af182
7 changed files with 722 additions and 157 deletions
+28
View File
@@ -33,6 +33,7 @@ from app.models.message import Message
from app.schemas.fascicolo import (
FascicoloAddMessagesRequest,
FascicoloCreate,
FascicoloDeadlineResponse,
FascicoloMessageItem,
FascicoloRemoveMessagesRequest,
FascicoloResponse,
@@ -73,6 +74,33 @@ async def list_fascicoli(
return [_to_response(f, cnt) for f, cnt in rows]
@router.get("/fascicoli/scadenzario", response_model=list[FascicoloDeadlineResponse])
async def list_fascicoli_scadenzario(
current_user: CurrentUser,
db: DB,
days_ahead: int = Query(30, ge=1, le=365, description="Giorni da considerare in avanti"),
include_overdue: bool = Query(True, description="Includi fascicoli gia' scaduti"),
) -> list[FascicoloDeadlineResponse]:
"""
Scadenzario pratiche: fascicoli con scadenza imminente o scaduta.
Ordinati per scadenza ASC (scaduti prima, poi futuri).
"""
svc = FascicoloService(db)
rows = await svc.list_fascicoli_scadenzario(
current_user.tenant_id,
days_ahead=days_ahead,
include_overdue=include_overdue,
)
items = []
for fascicolo, cnt, is_overdue in rows:
resp = FascicoloDeadlineResponse.model_validate(fascicolo)
resp.message_count = cnt
resp.is_overdue = is_overdue
items.append(resp)
return items
@router.post("/fascicoli", response_model=FascicoloResponse, status_code=status.HTTP_201_CREATED)
async def create_fascicolo(
data: FascicoloCreate,
+21
View File
@@ -90,3 +90,24 @@ class MessageFascicoloSummary(BaseModel):
categoria: Optional[str] = None
model_config = {"from_attributes": True}
# ─── Scadenzario fascicoli ────────────────────────────────────────────────────
class FascicoloDeadlineResponse(BaseModel):
"""Fascicolo con scadenza per lo scadenzario pratiche."""
id: uuid.UUID
tenant_id: uuid.UUID
titolo: str
numero_pratica: Optional[str] = None
stato: str
categoria: Optional[str] = None
responsabile_id: Optional[uuid.UUID] = None
scadenza: Optional[datetime] = None
note: Optional[str] = None
created_at: datetime
updated_at: datetime
message_count: int = 0
is_overdue: bool = False
model_config = {"from_attributes": True}
+55 -1
View File
@@ -3,7 +3,7 @@ Service per la gestione dei Fascicoli (fascicolazione pratiche).
"""
import uuid
from datetime import datetime
from datetime import datetime, timedelta, timezone
from sqlalchemy import delete, func, select
from sqlalchemy.ext.asyncio import AsyncSession
@@ -268,3 +268,57 @@ class FascicoloService:
.order_by(Fascicolo.titolo)
)
return list(result.scalars().all())
# ─── Scadenzario fascicoli ────────────────────────────────────────────────
async def list_fascicoli_scadenzario(
self,
tenant_id: uuid.UUID,
days_ahead: int = 30,
include_overdue: bool = True,
) -> list[tuple[Fascicolo, int, bool]]:
"""
Restituisce lista di (Fascicolo, message_count, is_overdue) per lo scadenzario.
Filtra fascicoli con scadenza impostata nel range richiesto.
Ordinati: scaduti prima (ASC scadenza), poi futuri (ASC scadenza).
"""
now = datetime.now(timezone.utc)
future_limit = now + timedelta(days=days_ahead)
count_sub = (
select(
FascicoloMessage.fascicolo_id,
func.count(FascicoloMessage.message_id).label("cnt"),
)
.group_by(FascicoloMessage.fascicolo_id)
.subquery()
)
stmt = (
select(Fascicolo, func.coalesce(count_sub.c.cnt, 0).label("message_count"))
.outerjoin(count_sub, Fascicolo.id == count_sub.c.fascicolo_id)
.where(
Fascicolo.tenant_id == tenant_id,
Fascicolo.scadenza.is_not(None),
)
.order_by(Fascicolo.scadenza.asc())
)
if include_overdue:
# Scaduti (qualsiasi data passata) + futuri fino al limite
stmt = stmt.where(Fascicolo.scadenza <= future_limit)
else:
# Solo scadenze future entro il limite
stmt = stmt.where(
Fascicolo.scadenza > now,
Fascicolo.scadenza <= future_limit,
)
result = await self.db.execute(stmt)
rows = result.all()
return [
(fascicolo, int(cnt), fascicolo.scadenza < now if fascicolo.scadenza else False)
for fascicolo, cnt in rows
]