Fascicoli+Tassonomia+permessi
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
"""
|
||||
Router Fascicoli — fascicolazione pratiche.
|
||||
|
||||
Endpoint:
|
||||
- GET /fascicoli – lista fascicoli (con filtri)
|
||||
- POST /fascicoli – crea un fascicolo
|
||||
- GET /fascicoli/{id} – dettaglio fascicolo
|
||||
- PATCH /fascicoli/{id} – modifica fascicolo
|
||||
- DELETE /fascicoli/{id} – elimina fascicolo (solo admin)
|
||||
|
||||
- GET /fascicoli/{id}/messages – messaggi del fascicolo
|
||||
- POST /fascicoli/{id}/messages – aggiungi messaggi al fascicolo
|
||||
- DELETE /fascicoli/{id}/messages – rimuovi messaggi dal fascicolo
|
||||
|
||||
- GET /messages/{message_id}/fascicoli – fascicoli di un messaggio
|
||||
|
||||
Permessi:
|
||||
- Tutti gli utenti autenticati possono creare fascicoli.
|
||||
- PATCH: creatore o admin.
|
||||
- DELETE fascicolo: solo admin.
|
||||
- Operazioni su messaggi: utente con accesso al tenant.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Query, status
|
||||
from sqlalchemy import select
|
||||
|
||||
from app.core.exceptions import ForbiddenError, NotFoundError
|
||||
from app.dependencies import AdminUser, CurrentUser, DB
|
||||
from app.models.message import Message
|
||||
from app.schemas.fascicolo import (
|
||||
FascicoloAddMessagesRequest,
|
||||
FascicoloCreate,
|
||||
FascicoloMessageItem,
|
||||
FascicoloRemoveMessagesRequest,
|
||||
FascicoloResponse,
|
||||
FascicoloUpdate,
|
||||
MessageFascicoloSummary,
|
||||
)
|
||||
from app.services.fascicolo_service import FascicoloService
|
||||
|
||||
router = APIRouter(tags=["Fascicoli"])
|
||||
|
||||
|
||||
# ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
def _to_response(fascicolo, message_count: int) -> FascicoloResponse:
|
||||
resp = FascicoloResponse.model_validate(fascicolo)
|
||||
resp.message_count = int(message_count)
|
||||
return resp
|
||||
|
||||
|
||||
# ─── CRUD Fascicolo ───────────────────────────────────────────────────────────
|
||||
|
||||
@router.get("/fascicoli", response_model=list[FascicoloResponse])
|
||||
async def list_fascicoli(
|
||||
current_user: CurrentUser,
|
||||
db: DB,
|
||||
stato: Optional[str] = Query(None, pattern=r"^(aperto|chiuso|archiviato)$"),
|
||||
responsabile_id: Optional[uuid.UUID] = Query(None),
|
||||
search: Optional[str] = Query(None, max_length=200),
|
||||
) -> list[FascicoloResponse]:
|
||||
"""Elenca i fascicoli del tenant con filtri opzionali."""
|
||||
svc = FascicoloService(db)
|
||||
rows = await svc.list_fascicoli(
|
||||
current_user.tenant_id,
|
||||
stato=stato,
|
||||
responsabile_id=responsabile_id,
|
||||
search=search,
|
||||
)
|
||||
return [_to_response(f, cnt) for f, cnt in rows]
|
||||
|
||||
|
||||
@router.post("/fascicoli", response_model=FascicoloResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def create_fascicolo(
|
||||
data: FascicoloCreate,
|
||||
current_user: CurrentUser,
|
||||
db: DB,
|
||||
) -> FascicoloResponse:
|
||||
"""Crea un nuovo fascicolo."""
|
||||
svc = FascicoloService(db)
|
||||
fascicolo, cnt = await svc.create_fascicolo(
|
||||
current_user.tenant_id, data, created_by=current_user.id
|
||||
)
|
||||
return _to_response(fascicolo, cnt)
|
||||
|
||||
|
||||
@router.get("/fascicoli/{fascicolo_id}", response_model=FascicoloResponse)
|
||||
async def get_fascicolo(
|
||||
fascicolo_id: uuid.UUID,
|
||||
current_user: CurrentUser,
|
||||
db: DB,
|
||||
) -> FascicoloResponse:
|
||||
"""Restituisce il dettaglio di un fascicolo."""
|
||||
svc = FascicoloService(db)
|
||||
fascicolo, cnt = await svc.get_fascicolo(current_user.tenant_id, fascicolo_id)
|
||||
return _to_response(fascicolo, cnt)
|
||||
|
||||
|
||||
@router.patch("/fascicoli/{fascicolo_id}", response_model=FascicoloResponse)
|
||||
async def update_fascicolo(
|
||||
fascicolo_id: uuid.UUID,
|
||||
data: FascicoloUpdate,
|
||||
current_user: CurrentUser,
|
||||
db: DB,
|
||||
) -> FascicoloResponse:
|
||||
"""
|
||||
Modifica un fascicolo.
|
||||
Solo il creatore o un amministratore possono modificarlo.
|
||||
"""
|
||||
svc = FascicoloService(db)
|
||||
fascicolo, cnt = await svc.update_fascicolo(
|
||||
current_user.tenant_id,
|
||||
fascicolo_id,
|
||||
data,
|
||||
current_user_id=current_user.id,
|
||||
is_admin=current_user.is_admin,
|
||||
)
|
||||
return _to_response(fascicolo, cnt)
|
||||
|
||||
|
||||
@router.delete("/fascicoli/{fascicolo_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_fascicolo(
|
||||
fascicolo_id: uuid.UUID,
|
||||
current_user: AdminUser,
|
||||
db: DB,
|
||||
) -> None:
|
||||
"""Elimina un fascicolo (solo admin). I messaggi non vengono eliminati."""
|
||||
svc = FascicoloService(db)
|
||||
await svc.delete_fascicolo(current_user.tenant_id, fascicolo_id)
|
||||
|
||||
|
||||
# ─── Messaggi del fascicolo ───────────────────────────────────────────────────
|
||||
|
||||
@router.get("/fascicoli/{fascicolo_id}/messages", response_model=list[FascicoloMessageItem])
|
||||
async def get_fascicolo_messages(
|
||||
fascicolo_id: uuid.UUID,
|
||||
current_user: CurrentUser,
|
||||
db: DB,
|
||||
) -> list[FascicoloMessageItem]:
|
||||
"""Restituisce i messaggi collegati al fascicolo."""
|
||||
svc = FascicoloService(db)
|
||||
rows = await svc.get_fascicolo_messages(current_user.tenant_id, fascicolo_id)
|
||||
items = []
|
||||
for msg, added_at in rows:
|
||||
item = FascicoloMessageItem(
|
||||
id=msg.id,
|
||||
subject=msg.subject,
|
||||
from_address=msg.from_address,
|
||||
to_addresses=msg.to_addresses,
|
||||
direction=msg.direction,
|
||||
pec_type=msg.pec_type,
|
||||
state=msg.state,
|
||||
mailbox_id=msg.mailbox_id,
|
||||
received_at=msg.received_at,
|
||||
sent_at=msg.sent_at,
|
||||
created_at=msg.created_at,
|
||||
added_at=added_at,
|
||||
)
|
||||
items.append(item)
|
||||
return items
|
||||
|
||||
|
||||
@router.post("/fascicoli/{fascicolo_id}/messages", response_model=dict)
|
||||
async def add_messages_to_fascicolo(
|
||||
fascicolo_id: uuid.UUID,
|
||||
data: FascicoloAddMessagesRequest,
|
||||
current_user: CurrentUser,
|
||||
db: DB,
|
||||
) -> dict:
|
||||
"""
|
||||
Aggiunge messaggi al fascicolo.
|
||||
Ignora i messaggi non del tenant o gia' presenti.
|
||||
"""
|
||||
svc = FascicoloService(db)
|
||||
added = await svc.add_messages(
|
||||
current_user.tenant_id,
|
||||
fascicolo_id,
|
||||
data.message_ids,
|
||||
added_by=current_user.id,
|
||||
)
|
||||
return {"added": added}
|
||||
|
||||
|
||||
@router.delete("/fascicoli/{fascicolo_id}/messages", response_model=dict)
|
||||
async def remove_messages_from_fascicolo(
|
||||
fascicolo_id: uuid.UUID,
|
||||
data: FascicoloRemoveMessagesRequest,
|
||||
current_user: CurrentUser,
|
||||
db: DB,
|
||||
) -> dict:
|
||||
"""Rimuove messaggi dal fascicolo (non li elimina dalla posta)."""
|
||||
svc = FascicoloService(db)
|
||||
removed = await svc.remove_messages(
|
||||
current_user.tenant_id,
|
||||
fascicolo_id,
|
||||
data.message_ids,
|
||||
)
|
||||
return {"removed": removed}
|
||||
|
||||
|
||||
# ─── Fascicoli di un messaggio ────────────────────────────────────────────────
|
||||
|
||||
@router.get("/messages/{message_id}/fascicoli", response_model=list[MessageFascicoloSummary])
|
||||
async def get_message_fascicoli(
|
||||
message_id: uuid.UUID,
|
||||
current_user: CurrentUser,
|
||||
db: DB,
|
||||
) -> list[MessageFascicoloSummary]:
|
||||
"""Restituisce i fascicoli a cui appartiene un messaggio."""
|
||||
# Verifica che il messaggio esista nel tenant
|
||||
result = await db.execute(
|
||||
select(Message).where(
|
||||
Message.id == message_id,
|
||||
Message.tenant_id == current_user.tenant_id,
|
||||
)
|
||||
)
|
||||
if not result.scalar_one_or_none():
|
||||
raise NotFoundError(f"Messaggio {message_id} non trovato")
|
||||
|
||||
svc = FascicoloService(db)
|
||||
fascicoli = await svc.get_message_fascicoli(current_user.tenant_id, message_id)
|
||||
return [MessageFascicoloSummary.model_validate(f) for f in fascicoli]
|
||||
Reference in New Issue
Block a user