mirror of
https://github.com/idrainformatica/PecFlow.git
synced 2026-06-16 12:45:42 +02:00
Modifiche varie
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
"""
|
||||
Service layer per la gestione delle firme automatiche.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from typing import Sequence
|
||||
|
||||
from sqlalchemy import select, func
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.exceptions import NotFoundError, ValidationError
|
||||
from app.models.signature import Signature, SignatureAssignment
|
||||
from app.schemas.signature import SignatureCreate, SignatureUpdate, SignatureAssignmentCreate
|
||||
|
||||
|
||||
class SignatureService:
|
||||
def __init__(self, db: AsyncSession) -> None:
|
||||
self.db = db
|
||||
|
||||
# ─── Firme ────────────────────────────────────────────────────────────────
|
||||
|
||||
async def list_signatures(
|
||||
self, tenant_id: uuid.UUID, q: str | None = None
|
||||
) -> tuple[Sequence[Signature], int]:
|
||||
stmt = select(Signature).where(Signature.tenant_id == tenant_id)
|
||||
if q:
|
||||
stmt = stmt.where(Signature.name.ilike(f"%{q}%"))
|
||||
stmt = stmt.order_by(Signature.name)
|
||||
|
||||
count_stmt = select(func.count()).select_from(stmt.subquery())
|
||||
total = (await self.db.execute(count_stmt)).scalar_one()
|
||||
items = (await self.db.execute(stmt)).scalars().all()
|
||||
return items, total
|
||||
|
||||
async def get_signature(
|
||||
self, tenant_id: uuid.UUID, signature_id: uuid.UUID
|
||||
) -> Signature:
|
||||
stmt = select(Signature).where(
|
||||
Signature.id == signature_id,
|
||||
Signature.tenant_id == tenant_id,
|
||||
)
|
||||
sig = (await self.db.execute(stmt)).scalar_one_or_none()
|
||||
if sig is None:
|
||||
raise NotFoundError("Firma non trovata")
|
||||
return sig
|
||||
|
||||
async def create_signature(
|
||||
self,
|
||||
tenant_id: uuid.UUID,
|
||||
data: SignatureCreate,
|
||||
created_by: uuid.UUID | None = None,
|
||||
) -> Signature:
|
||||
sig = Signature(
|
||||
tenant_id=tenant_id,
|
||||
name=data.name,
|
||||
description=data.description,
|
||||
body_html=data.body_html,
|
||||
body_text=data.body_text,
|
||||
created_by=created_by,
|
||||
)
|
||||
self.db.add(sig)
|
||||
await self.db.commit()
|
||||
await self.db.refresh(sig)
|
||||
return sig
|
||||
|
||||
async def update_signature(
|
||||
self,
|
||||
tenant_id: uuid.UUID,
|
||||
signature_id: uuid.UUID,
|
||||
data: SignatureUpdate,
|
||||
) -> Signature:
|
||||
sig = await self.get_signature(tenant_id, signature_id)
|
||||
if data.name is not None:
|
||||
sig.name = data.name
|
||||
if data.description is not None:
|
||||
sig.description = data.description
|
||||
if data.body_html is not None:
|
||||
sig.body_html = data.body_html
|
||||
if data.body_text is not None:
|
||||
sig.body_text = data.body_text
|
||||
await self.db.commit()
|
||||
await self.db.refresh(sig)
|
||||
return sig
|
||||
|
||||
async def delete_signature(
|
||||
self, tenant_id: uuid.UUID, signature_id: uuid.UUID
|
||||
) -> None:
|
||||
sig = await self.get_signature(tenant_id, signature_id)
|
||||
await self.db.delete(sig)
|
||||
await self.db.commit()
|
||||
|
||||
# ─── Assegnazioni ─────────────────────────────────────────────────────────
|
||||
|
||||
async def list_assignments(
|
||||
self,
|
||||
tenant_id: uuid.UUID,
|
||||
mailbox_id: uuid.UUID | None = None,
|
||||
virtual_box_id: uuid.UUID | None = None,
|
||||
) -> tuple[list[dict], int]:
|
||||
"""
|
||||
Restituisce le assegnazioni con il nome della firma incluso.
|
||||
Filtro opzionale per casella o virtual box.
|
||||
"""
|
||||
stmt = (
|
||||
select(SignatureAssignment, Signature.name.label("signature_name"))
|
||||
.join(Signature, Signature.id == SignatureAssignment.signature_id)
|
||||
.where(SignatureAssignment.tenant_id == tenant_id)
|
||||
)
|
||||
if mailbox_id:
|
||||
stmt = stmt.where(SignatureAssignment.mailbox_id == mailbox_id)
|
||||
if virtual_box_id:
|
||||
stmt = stmt.where(SignatureAssignment.virtual_box_id == virtual_box_id)
|
||||
stmt = stmt.order_by(SignatureAssignment.created_at)
|
||||
|
||||
rows = (await self.db.execute(stmt)).all()
|
||||
items = []
|
||||
for row in rows:
|
||||
assignment: SignatureAssignment = row[0]
|
||||
sig_name: str = row[1]
|
||||
items.append({
|
||||
"id": assignment.id,
|
||||
"tenant_id": assignment.tenant_id,
|
||||
"signature_id": assignment.signature_id,
|
||||
"mailbox_id": assignment.mailbox_id,
|
||||
"virtual_box_id": assignment.virtual_box_id,
|
||||
"context": assignment.context,
|
||||
"created_at": assignment.created_at,
|
||||
"signature_name": sig_name,
|
||||
})
|
||||
return items, len(items)
|
||||
|
||||
async def create_assignment(
|
||||
self,
|
||||
tenant_id: uuid.UUID,
|
||||
data: SignatureAssignmentCreate,
|
||||
) -> dict:
|
||||
# Validazione: esattamente uno tra mailbox_id e virtual_box_id
|
||||
if bool(data.mailbox_id) == bool(data.virtual_box_id):
|
||||
raise ValidationError(
|
||||
"Specificare esattamente uno tra mailbox_id e virtual_box_id"
|
||||
)
|
||||
|
||||
# Verifica che la firma esista nel tenant
|
||||
await self.get_signature(tenant_id, data.signature_id)
|
||||
|
||||
# Rimuovi eventuale assegnazione precedente per la stessa coppia (target, context)
|
||||
existing_stmt = select(SignatureAssignment).where(
|
||||
SignatureAssignment.tenant_id == tenant_id,
|
||||
SignatureAssignment.context == data.context,
|
||||
)
|
||||
if data.mailbox_id:
|
||||
existing_stmt = existing_stmt.where(
|
||||
SignatureAssignment.mailbox_id == data.mailbox_id
|
||||
)
|
||||
else:
|
||||
existing_stmt = existing_stmt.where(
|
||||
SignatureAssignment.virtual_box_id == data.virtual_box_id
|
||||
)
|
||||
existing = (await self.db.execute(existing_stmt)).scalar_one_or_none()
|
||||
if existing:
|
||||
await self.db.delete(existing)
|
||||
await self.db.flush() # Flush il DELETE prima dell'INSERT per evitare UniqueViolationError
|
||||
|
||||
assignment = SignatureAssignment(
|
||||
tenant_id=tenant_id,
|
||||
signature_id=data.signature_id,
|
||||
mailbox_id=data.mailbox_id,
|
||||
virtual_box_id=data.virtual_box_id,
|
||||
context=data.context,
|
||||
)
|
||||
self.db.add(assignment)
|
||||
await self.db.commit()
|
||||
await self.db.refresh(assignment)
|
||||
|
||||
# Carica il nome della firma per la risposta
|
||||
sig = await self.get_signature(tenant_id, data.signature_id)
|
||||
return {
|
||||
"id": assignment.id,
|
||||
"tenant_id": assignment.tenant_id,
|
||||
"signature_id": assignment.signature_id,
|
||||
"mailbox_id": assignment.mailbox_id,
|
||||
"virtual_box_id": assignment.virtual_box_id,
|
||||
"context": assignment.context,
|
||||
"created_at": assignment.created_at,
|
||||
"signature_name": sig.name,
|
||||
}
|
||||
|
||||
async def delete_assignment(
|
||||
self, tenant_id: uuid.UUID, assignment_id: uuid.UUID
|
||||
) -> None:
|
||||
stmt = select(SignatureAssignment).where(
|
||||
SignatureAssignment.id == assignment_id,
|
||||
SignatureAssignment.tenant_id == tenant_id,
|
||||
)
|
||||
assignment = (await self.db.execute(stmt)).scalar_one_or_none()
|
||||
if assignment is None:
|
||||
raise NotFoundError("Assegnazione firma non trovata")
|
||||
await self.db.delete(assignment)
|
||||
await self.db.commit()
|
||||
|
||||
async def resolve_signature(
|
||||
self,
|
||||
tenant_id: uuid.UUID,
|
||||
context: str,
|
||||
mailbox_id: uuid.UUID | None = None,
|
||||
virtual_box_id: uuid.UUID | None = None,
|
||||
) -> Signature | None:
|
||||
"""
|
||||
Restituisce la firma assegnata per casella/vbox nel contesto specificato.
|
||||
Cerca prima un'assegnazione con context == context, poi context == 'both'.
|
||||
"""
|
||||
if not mailbox_id and not virtual_box_id:
|
||||
return None
|
||||
|
||||
stmt = (
|
||||
select(SignatureAssignment)
|
||||
.where(
|
||||
SignatureAssignment.tenant_id == tenant_id,
|
||||
SignatureAssignment.context.in_([context, "both"]),
|
||||
)
|
||||
)
|
||||
if mailbox_id:
|
||||
stmt = stmt.where(SignatureAssignment.mailbox_id == mailbox_id)
|
||||
else:
|
||||
stmt = stmt.where(SignatureAssignment.virtual_box_id == virtual_box_id)
|
||||
|
||||
assignments = (await self.db.execute(stmt)).scalars().all()
|
||||
if not assignments:
|
||||
return None
|
||||
|
||||
# Preferisce il match esatto sul contesto rispetto a 'both'
|
||||
exact = next((a for a in assignments if a.context == context), None)
|
||||
assignment = exact or assignments[0]
|
||||
|
||||
return await self.get_signature(tenant_id, assignment.signature_id)
|
||||
|
||||
async def delete_assignment_by_target(
|
||||
self,
|
||||
tenant_id: uuid.UUID,
|
||||
context: str,
|
||||
mailbox_id: uuid.UUID | None = None,
|
||||
virtual_box_id: uuid.UUID | None = None,
|
||||
) -> None:
|
||||
"""Rimuove l'assegnazione per una specifica casella/vbox+contesto (se presente)."""
|
||||
stmt = select(SignatureAssignment).where(
|
||||
SignatureAssignment.tenant_id == tenant_id,
|
||||
SignatureAssignment.context == context,
|
||||
)
|
||||
if mailbox_id:
|
||||
stmt = stmt.where(SignatureAssignment.mailbox_id == mailbox_id)
|
||||
elif virtual_box_id:
|
||||
stmt = stmt.where(SignatureAssignment.virtual_box_id == virtual_box_id)
|
||||
else:
|
||||
return
|
||||
assignment = (await self.db.execute(stmt)).scalar_one_or_none()
|
||||
if assignment:
|
||||
await self.db.delete(assignment)
|
||||
await self.db.commit()
|
||||
Reference in New Issue
Block a user