Asunto en internos funcionando
This commit is contained in:
@@ -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},
|
||||
)
|
||||
@@ -63,6 +63,29 @@ async def read_users(
|
||||
result = await db.execute(query)
|
||||
return result.scalars().all()
|
||||
|
||||
@router.get("/taggable", response_model=List[UserResponse])
|
||||
async def get_taggable_users(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(deps.get_current_user)
|
||||
):
|
||||
"""
|
||||
Obtener usuarios que se pueden etiquetar en asuntos.
|
||||
- Staff interno + CLIENT_ADMIN → todos los usuarios activos
|
||||
- CLIENT_USER → solo CLIENT_ADMIN de su tenant
|
||||
"""
|
||||
if current_user.role.is_global or current_user.role == UserRole.CLIENT_ADMIN:
|
||||
query = select(User).where(User.is_active == True)
|
||||
else:
|
||||
query = select(User).where(
|
||||
User.tenant_id == current_user.tenant_id,
|
||||
User.role == UserRole.CLIENT_ADMIN,
|
||||
User.is_active == True
|
||||
)
|
||||
|
||||
query = query.order_by(User.first_name)
|
||||
result = await db.execute(query)
|
||||
return result.scalars().all()
|
||||
|
||||
|
||||
@router.post("/", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def create_user(
|
||||
@@ -402,3 +425,4 @@ async def activate_user(
|
||||
await db.commit()
|
||||
await db.refresh(db_user)
|
||||
return db_user
|
||||
|
||||
|
||||
Reference in New Issue
Block a user