feature/pedimentos-correccion-partidas
This commit is contained in:
@@ -16,6 +16,7 @@ from .tasks.auditoria import (
|
||||
)
|
||||
from .tasks.internal_services import auditar_pedimentos
|
||||
from .tasks.microservice_v2 import procesar_pedimentos_completos, procesar_pedimento_completo_individual
|
||||
from .tasks.auto_corregir import auto_corregir_pedamentos_task, auditar_pedamentos_incompletos_task
|
||||
from api.customs.models import Pedimento
|
||||
from api.organization.models import Organizacion
|
||||
from api.record.models import Document
|
||||
@@ -98,7 +99,7 @@ def crear_partidas_organizacion(request):
|
||||
if not user.is_superuser and str(user.organizacion.id) != organizacion_id:
|
||||
return Response({'error': 'No tiene permisos para esta organización'}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
task = crear_partidas.delay(organizacion_id)
|
||||
task = crear_partidas.delay(organizacion_id, user_id=str(user.id))
|
||||
|
||||
return Response({
|
||||
'organizacion_id': organizacion_id,
|
||||
@@ -228,7 +229,7 @@ def auditar_pedimentos_endpoint(request):
|
||||
)
|
||||
|
||||
# Ejecutar la tarea de auditoría
|
||||
task = auditar_pedimentos.delay(organizacion_id)
|
||||
task = auditar_pedimentos.delay(organizacion_id, user_id=str(user.id))
|
||||
message = f"Auditoría iniciada para la organización {organizacion_id}"
|
||||
|
||||
return Response({
|
||||
@@ -309,7 +310,7 @@ def auditar_procesamiento_remesa_pedimento_endpoint(request):
|
||||
|
||||
|
||||
def _lanzar_auditoria_organizacion(request, task_fn, label):
|
||||
"""Helper compartido para los 4 endpoints de auditoría masiva por organización."""
|
||||
"""Helper compartido para los endpoints de auditoría masiva por organización."""
|
||||
organizacion_id = request.data.get('organizacion_id')
|
||||
if not organizacion_id:
|
||||
return Response({'error': 'Debe proporcionar organizacion_id'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
@@ -318,12 +319,12 @@ def _lanzar_auditoria_organizacion(request, task_fn, label):
|
||||
if not user.is_superuser and str(user.organizacion.id) != organizacion_id:
|
||||
return Response({'error': 'No tiene permisos para esta organización'}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
task = task_fn.delay(organizacion_id)
|
||||
task = task_fn.delay(organizacion_id, user_id=str(user.id))
|
||||
return Response({
|
||||
'organizacion_id': organizacion_id,
|
||||
'auditoria': label,
|
||||
'task_id': task.id,
|
||||
'mensaje': f'Auditoría de {label} iniciada. Consulta el resultado en GET /api/tasks/status/{task.id}/',
|
||||
'mensaje': f'Auditoría de {label} iniciada. Usa el stream SSE para seguimiento en tiempo real.',
|
||||
}, status=status.HTTP_202_ACCEPTED)
|
||||
|
||||
|
||||
@@ -1973,12 +1974,14 @@ def procesar_pedimento_completo_endpoint(request):
|
||||
'pc_descargado': pc_descargado,
|
||||
}
|
||||
|
||||
# Ya descargado — devolver diagnóstico sin encolar
|
||||
if pc_descargado:
|
||||
force = bool(request.data.get('force', False))
|
||||
|
||||
# Ya descargado — solo bloquear si no es forzado
|
||||
if pc_descargado and not force:
|
||||
return Response({
|
||||
**base_response,
|
||||
'estado': 'ya_descargado',
|
||||
'mensaje': 'El pedimento completo ya fue descargado',
|
||||
'mensaje': 'El pedimento completo ya fue descargado. Usa force=true para reprocesar remesas, partidas y documentos derivados.',
|
||||
}, status=status.HTTP_200_OK)
|
||||
|
||||
# No puede procesar — devolver diagnóstico con razones
|
||||
@@ -1990,7 +1993,7 @@ def procesar_pedimento_completo_endpoint(request):
|
||||
}, status=status.HTTP_200_OK)
|
||||
|
||||
# Todo en orden — encolar
|
||||
task = procesar_pedimento_completo_individual.delay(str(pedimento_id))
|
||||
task = procesar_pedimento_completo_individual.delay(str(pedimento_id), force=force)
|
||||
logger.info(f"Procesamiento PC encolado: {pedimento.pedimento} (task={task.id})")
|
||||
|
||||
return Response({
|
||||
@@ -2091,8 +2094,226 @@ def actualizar_info_pedimento(pedimento, info_xml):
|
||||
if actualizado:
|
||||
pedimento.save()
|
||||
return True
|
||||
|
||||
|
||||
return False
|
||||
|
||||
|
||||
except Exception:
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Auto-corrección de pedimentos incompletos
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@swagger_auto_schema(
|
||||
method='post',
|
||||
operation_description=(
|
||||
"Encola una tarea Celery que analiza los XMLs de pedimentos con "
|
||||
"consultar_vucem=False, extrae datos del pedimento completo VUCEM y "
|
||||
"auto-corrige los campos faltantes (numero_operacion, aduana, "
|
||||
"clave_pedimento, regimen, contribuyente). El documento se reclasifica "
|
||||
"a tipo 2 (Pedimento Completo) y se activa consultar_vucem=True."
|
||||
),
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
required=['organizacion_id'],
|
||||
properties={
|
||||
'organizacion_id': openapi.Schema(
|
||||
type=openapi.TYPE_STRING,
|
||||
description='UUID de la organización a procesar',
|
||||
),
|
||||
},
|
||||
),
|
||||
responses={
|
||||
202: openapi.Response('Tarea encolada correctamente'),
|
||||
400: openapi.Response('organizacion_id faltante'),
|
||||
404: openapi.Response('Organización no encontrada'),
|
||||
},
|
||||
)
|
||||
@api_view(['POST'])
|
||||
@permission_classes([IsAuthenticated, require_permission('auditoria.view')])
|
||||
def auto_corregir_pedamentos_endpoint(request):
|
||||
organizacion_id = request.data.get('organizacion_id')
|
||||
if not organizacion_id:
|
||||
return Response(
|
||||
{'error': 'organizacion_id es requerido'},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
try:
|
||||
Organizacion.objects.get(id=organizacion_id)
|
||||
except Organizacion.DoesNotExist:
|
||||
return Response(
|
||||
{'error': 'Organización no encontrada'},
|
||||
status=status.HTTP_404_NOT_FOUND,
|
||||
)
|
||||
|
||||
task = auto_corregir_pedamentos_task.delay(str(organizacion_id))
|
||||
logger.info(
|
||||
f"[auto_corregir] tarea encolada — org={organizacion_id} task={task.id}"
|
||||
)
|
||||
|
||||
return Response(
|
||||
{
|
||||
'task_id': task.id,
|
||||
'organizacion': str(organizacion_id),
|
||||
'mensaje': (
|
||||
'Tarea encolada. Se analizarán los pedimentos con '
|
||||
'consultar_vucem=False de la organización.'
|
||||
),
|
||||
},
|
||||
status=status.HTTP_202_ACCEPTED,
|
||||
)
|
||||
|
||||
|
||||
@swagger_auto_schema(
|
||||
method='post',
|
||||
operation_description=(
|
||||
"Análisis de solo lectura: detecta pedimentos con consultar_vucem=False "
|
||||
"que podrían corregirse automáticamente. No modifica BD ni storage. "
|
||||
"Retorna el listado de pedimentos corregibles, los campos que cambiarían "
|
||||
"y el nuevo nombre de documento que se asignaría."
|
||||
),
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
required=['organizacion_id'],
|
||||
properties={
|
||||
'organizacion_id': openapi.Schema(
|
||||
type=openapi.TYPE_STRING,
|
||||
description='UUID de la organización a analizar',
|
||||
),
|
||||
},
|
||||
),
|
||||
responses={
|
||||
202: openapi.Response('Tarea de análisis encolada correctamente'),
|
||||
400: openapi.Response('organizacion_id faltante'),
|
||||
404: openapi.Response('Organización no encontrada'),
|
||||
},
|
||||
)
|
||||
@api_view(['POST'])
|
||||
@permission_classes([IsAuthenticated, require_permission('auditoria.view')])
|
||||
def auditar_pedamentos_incompletos_endpoint(request):
|
||||
organizacion_id = request.data.get('organizacion_id')
|
||||
if not organizacion_id:
|
||||
return Response(
|
||||
{'error': 'organizacion_id es requerido'},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
try:
|
||||
Organizacion.objects.get(id=organizacion_id)
|
||||
except Organizacion.DoesNotExist:
|
||||
return Response(
|
||||
{'error': 'Organización no encontrada'},
|
||||
status=status.HTTP_404_NOT_FOUND,
|
||||
)
|
||||
|
||||
task = auditar_pedamentos_incompletos_task.delay(str(organizacion_id))
|
||||
logger.info(
|
||||
f"[auditar_incompletos] tarea encolada — org={organizacion_id} task={task.id}"
|
||||
)
|
||||
|
||||
return Response(
|
||||
{
|
||||
'task_id': task.id,
|
||||
'organizacion': str(organizacion_id),
|
||||
'mensaje': (
|
||||
'Tarea de análisis encolada. Se reportarán los pedimentos con '
|
||||
'consultar_vucem=False que podrían corregirse automáticamente, '
|
||||
'sin modificar nada.'
|
||||
),
|
||||
},
|
||||
status=status.HTTP_202_ACCEPTED,
|
||||
)
|
||||
|
||||
|
||||
@swagger_auto_schema(
|
||||
method='post',
|
||||
operation_description="Analiza un pedimento específico para auto-corrección (solo lectura, sin modificar BD). Acepta pedimento_id (UUID) o pedimento_app.",
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'pedimento_id': openapi.Schema(type=openapi.TYPE_STRING, description='UUID del pedimento'),
|
||||
'pedimento_app': openapi.Schema(type=openapi.TYPE_STRING, description='Número de pedimento (ej: 21-80-3452-1004463)'),
|
||||
},
|
||||
),
|
||||
responses={
|
||||
202: openapi.Response('Tarea de análisis encolada'),
|
||||
400: openapi.Response('Parámetro faltante'),
|
||||
404: openapi.Response('Pedimento no encontrado'),
|
||||
},
|
||||
)
|
||||
@api_view(['POST'])
|
||||
@permission_classes([IsAuthenticated, require_permission('auditoria.view')])
|
||||
def auditar_pedamento_incompleto_endpoint(request):
|
||||
pedimento_id = request.data.get('pedimento_id')
|
||||
pedimento_app = request.data.get('pedimento_app')
|
||||
if not pedimento_id and not pedimento_app:
|
||||
return Response({'error': 'pedimento_id o pedimento_app es requerido'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
try:
|
||||
if pedimento_id:
|
||||
pedimento = Pedimento.objects.get(id=pedimento_id)
|
||||
else:
|
||||
pedimento = Pedimento.objects.get(pedimento_app=pedimento_app)
|
||||
except Pedimento.DoesNotExist:
|
||||
return Response({'error': 'Pedimento no encontrado'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
task = auditar_pedamentos_incompletos_task.delay(str(pedimento.organizacion_id), str(pedimento.id))
|
||||
logger.info(f"[auditar_incompletos] individual — ped={pedimento.pedimento_app} task={task.id}")
|
||||
|
||||
return Response(
|
||||
{
|
||||
'task_id': task.id,
|
||||
'pedimento_id': str(pedimento.id),
|
||||
'pedimento': pedimento.pedimento_app,
|
||||
'mensaje': 'Análisis individual encolado. Sin modificar nada.',
|
||||
},
|
||||
status=status.HTTP_202_ACCEPTED,
|
||||
)
|
||||
|
||||
|
||||
@swagger_auto_schema(
|
||||
method='post',
|
||||
operation_description="Auto-corrige un pedimento específico: extrae campos del XML y actualiza BD + storage. Acepta pedimento_id (UUID) o pedimento_app.",
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
'pedimento_id': openapi.Schema(type=openapi.TYPE_STRING, description='UUID del pedimento'),
|
||||
'pedimento_app': openapi.Schema(type=openapi.TYPE_STRING, description='Número de pedimento (ej: 21-80-3452-1004463)'),
|
||||
},
|
||||
),
|
||||
responses={
|
||||
202: openapi.Response('Tarea de corrección encolada'),
|
||||
400: openapi.Response('Parámetro faltante'),
|
||||
404: openapi.Response('Pedimento no encontrado'),
|
||||
},
|
||||
)
|
||||
@api_view(['POST'])
|
||||
@permission_classes([IsAuthenticated, require_permission('auditoria.view')])
|
||||
def auto_corregir_pedamento_endpoint(request):
|
||||
pedimento_id = request.data.get('pedimento_id')
|
||||
pedimento_app = request.data.get('pedimento_app')
|
||||
if not pedimento_id and not pedimento_app:
|
||||
return Response({'error': 'pedimento_id o pedimento_app es requerido'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
try:
|
||||
if pedimento_id:
|
||||
pedimento = Pedimento.objects.get(id=pedimento_id)
|
||||
else:
|
||||
pedimento = Pedimento.objects.get(pedimento_app=pedimento_app)
|
||||
except Pedimento.DoesNotExist:
|
||||
return Response({'error': 'Pedimento no encontrado'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
task = auto_corregir_pedamentos_task.delay(str(pedimento.organizacion_id), str(pedimento.id))
|
||||
logger.info(f"[auto_corregir] individual — ped={pedimento.pedimento_app} task={task.id}")
|
||||
|
||||
return Response(
|
||||
{
|
||||
'task_id': task.id,
|
||||
'pedimento_id': str(pedimento.id),
|
||||
'pedimento': pedimento.pedimento_app,
|
||||
'mensaje': 'Corrección individual encolada.',
|
||||
},
|
||||
status=status.HTTP_202_ACCEPTED,
|
||||
)
|
||||
Reference in New Issue
Block a user