from ninja import Router from ninja.errors import HttpError from apps.accounts.api import require_permission from apps.knowledge.governance import ensure_governed_asset from apps.knowledge.models import GovernedAssetType from apps.solutions.models import Solution, TicketSolutionLink from apps.solutions.schemas import ( AdoptSolutionIn, SolutionIn, SolutionOut, TicketSolutionLinkIn, TicketSolutionLinkOut, TicketSolutionOut, ) from apps.tickets.models import Ticket from apps.tickets.services import ( TicketDataError, TicketStatusError, ) from apps.tickets.services import ( adopt_solution_for_ticket as adopt_solution_for_ticket_service, ) router = Router(tags=["solutions"]) @router.post("", response=SolutionOut) def create_solution(request, payload: SolutionIn): require_permission(request, "solution.manage") solution = Solution.objects.create( title=payload.title, problem_signature=payload.problem_signature, related_system=payload.related_system, environment_scope=payload.environment_scope, root_cause=payload.root_cause, solution=payload.solution, risk_note=payload.risk_note, status="draft", allow_auto_resolve=payload.allow_auto_resolve, requires_human_action=payload.requires_human_action, requires_external_confirmation=payload.requires_external_confirmation, requires_production_change=payload.requires_production_change, created_by=request.user if request.user.is_authenticated else None, ) ensure_governed_asset( GovernedAssetType.SOLUTION, solution, actor=request.user if request.user.is_authenticated else None, ) return solution @router.get("", response=list[SolutionOut]) def list_solutions(request, status: str | None = None): require_permission(request, "solution.view") queryset = Solution.objects.all() if status: queryset = queryset.filter(status=status) return queryset[:100] @router.get("/tickets/{ticket_id}", response=list[TicketSolutionOut]) def list_ticket_solutions(request, ticket_id: int): require_permission(request, "solution.view") queryset = TicketSolutionLink.objects.select_related("solution", "ticket").filter( ticket_id=ticket_id ) return [ { "id": link.id, "ticket_id": link.ticket_id, "solution_id": link.solution_id, "link_type": link.link_type, "solution": link.solution, "created_at": link.created_at, } for link in queryset ] @router.get("/{solution_id}", response=SolutionOut) def get_solution(request, solution_id: int): require_permission(request, "solution.view") return Solution.objects.get(id=solution_id) @router.post("/{solution_id}/link-ticket", response=TicketSolutionLinkOut) def link_solution_to_ticket(request, solution_id: int, payload: TicketSolutionLinkIn): require_permission(request, "solution.manage") if payload.link_type == "adopted": raise HttpError(400, "Use adopt-ticket to adopt a solution") solution = Solution.objects.get(id=solution_id) ticket = Ticket.objects.get(id=payload.ticket_id) link, _ = TicketSolutionLink.objects.get_or_create( ticket=ticket, solution=solution, link_type=payload.link_type, defaults={ "agent_run_id": payload.agent_run_id, "created_by": request.user if request.user.is_authenticated else None, }, ) return { "id": link.id, "ticket_id": ticket.id, "solution_id": solution.id, "link_type": link.link_type, "solution_usage_count": solution.usage_count, "created_at": link.created_at, } @router.post("/{solution_id}/adopt-ticket", response=TicketSolutionLinkOut) def adopt_solution_for_ticket(request, solution_id: int, payload: AdoptSolutionIn): require_permission(request, "solution.manage") require_permission(request, "ticket.resolve") operator = request.user if request.user.is_authenticated else None try: solution = Solution.objects.get(id=solution_id) ticket, link = adopt_solution_for_ticket_service( Ticket.objects.get(id=payload.ticket_id), solution, operator, remark=payload.remark, replied_to_customer=payload.replied_to_customer, ) solution.refresh_from_db(fields=["usage_count"]) except (TicketDataError, TicketStatusError) as error: raise HttpError(400, str(error)) from error return { "id": link.id, "ticket_id": ticket.id, "solution_id": solution.id, "link_type": link.link_type, "solution_usage_count": solution.usage_count, "created_at": link.created_at, }