Asunto en internos funcionando

This commit is contained in:
2026-03-24 08:01:57 -06:00
parent daf3a524c7
commit 08816ab73a
7 changed files with 660 additions and 240 deletions

View File

@@ -7,8 +7,8 @@ from sqlalchemy.orm import selectinload
from typing import List, Optional
from datetime import datetime, timedelta
import uuid
from app.models.issue import TicketIssue, ticket_issue_tagged_users
from app.api.schemas.issue import IssueCreate, IssueResponse
from app.models.issue import TicketIssue, ticket_issue_tagged_users, IssueStatus
from app.api.schemas.issue import IssueCreate, IssueResponse, IssueStatusUpdate
from app.core.database import get_db
from app.api.deps import get_current_user, get_current_tenant
@@ -571,6 +571,7 @@ async def get_ticket_issues(
IssueResponse(
id=issue.id, ticket_id=issue.ticket_id, tenant_id=issue.tenant_id,
content=issue.content, priority=issue.priority.value,
status=issue.status.value,
created_by=issue.created_by,
created_by_name=f"{issue.created_by_user.first_name} {issue.created_by_user.last_name}",
tagged_users=[
@@ -657,6 +658,7 @@ async def create_ticket_issue(
return IssueResponse(
id=new_issue.id, ticket_id=new_issue.ticket_id, tenant_id=new_issue.tenant_id,
content=new_issue.content, priority=new_issue.priority.value,
status=new_issue.status.value,
created_by=new_issue.created_by,
created_by_name=f"{new_issue.created_by_user.first_name} {new_issue.created_by_user.last_name}",
tagged_users=[
@@ -709,4 +711,142 @@ async def upload_issue_attachment(
"message": "Adjunto subido correctamente",
"attachment_filename": db_issue.attachment_filename,
"attachment_mime_type": db_issue.attachment_mime_type,
}
}
@router.get("/{ticket_id}/issues/{issue_id}", response_model=IssueResponse)
async def get_issue_detail(
ticket_id: str,
issue_id: str,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Obtener detalle de un asunto. Accesible para todos los roles."""
ticket_uuid = validate_uuid_param(ticket_id, "ticket ID")
issue_uuid = validate_uuid_param(issue_id, "issue ID")
query = select(Ticket).where(Ticket.id == ticket_uuid)
if current_user.role != UserRole.ADMIN:
query = query.where(Ticket.tenant_id == current_user.tenant_id)
result = await db.execute(query)
if not result.scalars().first():
raise HTTPException(status_code=404, detail="Ticket no encontrado")
result = await db.execute(
select(TicketIssue)
.where(TicketIssue.id == issue_uuid, TicketIssue.ticket_id == ticket_uuid)
.options(
selectinload(TicketIssue.created_by_user),
selectinload(TicketIssue.tagged_users),
)
)
issue = result.scalars().first()
if not issue:
raise HTTPException(status_code=404, detail="Asunto no encontrado")
return IssueResponse(
id=issue.id, ticket_id=issue.ticket_id, tenant_id=issue.tenant_id,
content=issue.content, priority=issue.priority.value,
status=issue.status.value,
created_by=issue.created_by,
created_by_name=f"{issue.created_by_user.first_name} {issue.created_by_user.last_name}",
tagged_users=[
{"id": u.id, "full_name": f"{u.first_name} {u.last_name}", "email": u.email}
for u in issue.tagged_users
],
attachment_filename=issue.attachment_filename,
attachment_mime_type=issue.attachment_mime_type,
created_at=issue.created_at, updated_at=issue.updated_at,
)
@router.patch("/{ticket_id}/issues/{issue_id}/status", response_model=IssueResponse)
async def update_issue_status(
ticket_id: str,
issue_id: str,
data: IssueStatusUpdate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Actualizar status de un asunto."""
ticket_uuid = validate_uuid_param(ticket_id, "ticket ID")
issue_uuid = validate_uuid_param(issue_id, "issue ID")
if current_user.role != UserRole.ADMIN and current_user.role != UserRole.CLIENT_ADMIN:
raise HTTPException(status_code=403, detail="Solo administradores pueden cambiar el status")
result = await db.execute(
select(TicketIssue)
.where(TicketIssue.id == issue_uuid, TicketIssue.ticket_id == ticket_uuid)
.options(
selectinload(TicketIssue.created_by_user),
selectinload(TicketIssue.tagged_users),
)
)
issue = result.scalars().first()
if not issue:
raise HTTPException(status_code=404, detail="Asunto no encontrado")
issue.status = IssueStatus[data.status]
issue.updated_at = datetime.utcnow()
await db.commit()
await safe_audit_log(
db=db, tenant_id=current_user.tenant_id, user_id=current_user.id,
action="ticket.issue.status_update", resource_type="ticket_issue",
resource_id=issue.id,
new_values={"status": data.status},
)
return IssueResponse(
id=issue.id, ticket_id=issue.ticket_id, tenant_id=issue.tenant_id,
content=issue.content, priority=issue.priority.value,
status=issue.status.value,
created_by=issue.created_by,
created_by_name=f"{issue.created_by_user.first_name} {issue.created_by_user.last_name}",
tagged_users=[
{"id": u.id, "full_name": f"{u.first_name} {u.last_name}", "email": u.email}
for u in issue.tagged_users
],
attachment_filename=issue.attachment_filename,
attachment_mime_type=issue.attachment_mime_type,
created_at=issue.created_at, updated_at=issue.updated_at,
)
@router.delete("/{ticket_id}/issues/{issue_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_issue(
ticket_id: str,
issue_id: str,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Eliminar un asunto. Solo ADMIN global o CLIENT_ADMIN."""
ticket_uuid = validate_uuid_param(ticket_id, "ticket ID")
issue_uuid = validate_uuid_param(issue_id, "issue ID")
if current_user.role != UserRole.ADMIN and current_user.role != UserRole.CLIENT_ADMIN:
raise HTTPException(status_code=403, detail="Solo administradores pueden eliminar asuntos")
result = await db.execute(
select(TicketIssue).where(
TicketIssue.id == issue_uuid,
TicketIssue.ticket_id == ticket_uuid,
)
)
issue = result.scalars().first()
if not issue:
raise HTTPException(status_code=404, detail="Asunto no encontrado")
old_content = issue.content
old_status = issue.status.value
await db.delete(issue)
await db.commit()
await safe_audit_log(
db=db, tenant_id=current_user.tenant_id, user_id=current_user.id,
action="ticket.issue.delete", resource_type="ticket_issue",
resource_id=issue_uuid,
old_values={"content": old_content, "status": old_status},
)