278 lines
11 KiB
Python
278 lines
11 KiB
Python
from rest_framework.decorators import api_view, permission_classes
|
|
from rest_framework.permissions import IsAuthenticated
|
|
from api.customs.models import Pedimento, Cove, EDocument, Partida
|
|
from api.record.models import Document
|
|
from api.organization.models import Organizacion
|
|
from django.db.models import Count, Q
|
|
import csv
|
|
import io
|
|
from drf_yasg.utils import swagger_auto_schema
|
|
from drf_yasg import openapi
|
|
from .serializers import ExportModelSerializer
|
|
from rest_framework.response import Response
|
|
from django.http import HttpResponse
|
|
import openpyxl
|
|
from django.apps import apps
|
|
from rest_framework import status
|
|
from django.shortcuts import render
|
|
from rest_framework import viewsets
|
|
from rest_framework.views import APIView
|
|
from core.permissions import (
|
|
IsSameOrganization,
|
|
IsSameOrganizationDeveloper,
|
|
IsSameOrganizationAndAdmin,
|
|
IsSuperUser
|
|
)
|
|
|
|
|
|
def export_model_to_csv(request, model_name, fields, module='datastage', filters=None):
|
|
model = apps.get_model(module, model_name)
|
|
queryset = model.objects.filter(**(filters or {})).values(*fields)
|
|
response = HttpResponse(content_type='text/csv')
|
|
response['Content-Disposition'] = f'attachment; filename="{model_name}.csv"'
|
|
writer = csv.DictWriter(response, fieldnames=fields)
|
|
writer.writeheader()
|
|
for row in queryset:
|
|
writer.writerow(row)
|
|
return response
|
|
|
|
|
|
def export_model_to_excel(request, model_name, fields, module='datastage', filters=None):
|
|
model = apps.get_model(module, model_name)
|
|
queryset = model.objects.filter(**(filters or {})).values(*fields)
|
|
wb = openpyxl.Workbook()
|
|
ws = wb.active
|
|
ws.append(fields)
|
|
for row in queryset:
|
|
# Convertir cada valor a string para asegurar compatibilidad con Excel
|
|
row_values = []
|
|
for field in fields:
|
|
value = row[field]
|
|
# Si es UUID u otro objeto, convertirlo a string
|
|
if hasattr(value, '__str__'):
|
|
value = str(value)
|
|
row_values.append(value)
|
|
ws.append(row_values)
|
|
output = io.BytesIO()
|
|
wb.save(output)
|
|
output.seek(0)
|
|
response = HttpResponse(
|
|
output.read(),
|
|
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
)
|
|
response['Content-Disposition'] = f'attachment; filename="{model_name}.xlsx"'
|
|
return response
|
|
|
|
|
|
class ExportModelView(APIView):
|
|
my_tags = ['Reportes']
|
|
permission_classes = [IsAuthenticated & (
|
|
IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser
|
|
)]
|
|
|
|
@swagger_auto_schema(
|
|
manual_parameters=[
|
|
openapi.Parameter(
|
|
'model',
|
|
openapi.IN_QUERY,
|
|
description="Nombre del modelo (ejemplo: Registro500)",
|
|
type=openapi.TYPE_STRING,
|
|
required=True
|
|
)
|
|
],
|
|
responses={200: openapi.Response('Campos disponibles', schema=openapi.Schema(
|
|
type=openapi.TYPE_OBJECT,
|
|
properties={
|
|
'fields': openapi.Schema(
|
|
type=openapi.TYPE_ARRAY,
|
|
items=openapi.Items(type=openapi.TYPE_STRING)
|
|
)
|
|
}
|
|
))}
|
|
)
|
|
def get(self, request, *args, **kwargs):
|
|
"""
|
|
Devuelve los campos disponibles para el modelo solicitado.
|
|
Ejemplo: /api/reports/exportmodel/?model=Registro500
|
|
"""
|
|
model_name = request.query_params.get('model')
|
|
module = request.query_params.get('module', 'datastage')
|
|
if not model_name:
|
|
return Response({'error': 'model is required'}, status=status.HTTP_400_BAD_REQUEST)
|
|
try:
|
|
model = apps.get_model(module, model_name)
|
|
except LookupError:
|
|
return Response(
|
|
{'error': f'Model {model_name} not found in app {module}'},
|
|
status=status.HTTP_404_NOT_FOUND
|
|
)
|
|
fields = [f.name for f in model._meta.fields]
|
|
return Response({'fields': fields})
|
|
|
|
@swagger_auto_schema(
|
|
request_body=ExportModelSerializer,
|
|
responses={200: 'Archivo generado (Excel o CSV)'}
|
|
)
|
|
def post(self, request, *args, **kwargs):
|
|
model_name = request.data.get('model')
|
|
fields = request.data.get('fields')
|
|
export_type = request.data.get('type', 'csv')
|
|
module = request.data.get('module', 'datastage')
|
|
|
|
if not model_name or not fields:
|
|
return Response({'error': 'model and fields are required'}, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
# Aplicar filtro de organización
|
|
filters = {"organizacion__id": request.user.organizacion.id}
|
|
# Agregar filtros adicionales del request
|
|
filters.update(request.data.get('filters', {}))
|
|
|
|
if export_type == 'excel':
|
|
return export_model_to_excel(request, model_name, fields, module, filters)
|
|
else:
|
|
return export_model_to_csv(request, model_name, fields, module, filters)
|
|
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([IsAuthenticated])
|
|
def dashboard_summary(request):
|
|
org_id = request.query_params.get('organizacion_id')
|
|
filters = {}
|
|
user = request.user
|
|
|
|
pedimento_app = request.query_params.get('pedimento_app')
|
|
aduana = request.query_params.get('aduana')
|
|
patente = request.query_params.get('patente')
|
|
regimen = request.query_params.get('regimen')
|
|
agente_aduanal = request.query_params.get('agente_aduanal')
|
|
tipo_operacion = request.query_params.get('tipo_operacion')
|
|
fecha_pago_gte = request.query_params.get('fecha_pago__gte')
|
|
fecha_pago_lte = request.query_params.get('fecha_pago__lte')
|
|
contribuyente__rfc = request.query_params.get('contribuyente__rfc')
|
|
|
|
# Si no se especifica organización y el usuario tiene organización, usarla
|
|
if not org_id and hasattr(user, 'organizacion') and user.organizacion:
|
|
org_id = user.organizacion.id
|
|
# Si no es superusuario, filtrar por organización
|
|
if org_id and not getattr(user, 'is_superuser', False):
|
|
filters['organizacion_id'] = org_id
|
|
|
|
# Si el usuario pertenece al grupo Importador, filtrar por RFC
|
|
if user.groups.filter(name='Importador').exists():
|
|
rfc = getattr(user, 'rfc', None)
|
|
if rfc:
|
|
filters['contribuyente__rfc'] = rfc
|
|
|
|
if pedimento_app:
|
|
filters['pedimento_app'] = pedimento_app
|
|
if aduana:
|
|
filters['aduana'] = aduana
|
|
if patente:
|
|
filters['patente'] = patente
|
|
if regimen:
|
|
filters['regimen'] = regimen
|
|
if agente_aduanal:
|
|
filters['agente_aduanal'] = agente_aduanal
|
|
if tipo_operacion:
|
|
filters['tipo_operacion__tipo'] = tipo_operacion
|
|
if fecha_pago_gte:
|
|
filters['fecha_pago__gte'] = fecha_pago_gte
|
|
if fecha_pago_lte:
|
|
filters['fecha_pago__lte'] = fecha_pago_lte
|
|
if contribuyente__rfc:
|
|
filters['contribuyente__rfc'] = contribuyente__rfc
|
|
|
|
# Filtrar pedimentos
|
|
pedimentos_qs = Pedimento.objects.filter(**filters)
|
|
pedimentos_total = pedimentos_qs.count()
|
|
pedimentos_completos = pedimentos_qs.filter(existe_expediente=True).count()
|
|
pedimentos_pendientes = pedimentos_total - pedimentos_completos
|
|
|
|
# Usar los IDs de pedimentos filtrados para los demás modelos
|
|
pedimento_ids = list(pedimentos_qs.values_list('id', flat=True))
|
|
|
|
coves_total = Cove.objects.filter(pedimento_id__in=pedimento_ids).count()
|
|
coves_procesados = Cove.objects.filter(
|
|
pedimento_id__in=pedimento_ids,
|
|
cove_descargado=True
|
|
).count()
|
|
acuse_coves_procesados = Cove.objects.filter(
|
|
pedimento_id__in=pedimento_ids,
|
|
acuse_cove_descargado=True
|
|
).count()
|
|
acuse_coves_pendientes = coves_total - acuse_coves_procesados
|
|
coves_pendientes = coves_total - coves_procesados
|
|
|
|
edocs_total = EDocument.objects.filter(pedimento_id__in=pedimento_ids).count()
|
|
edocs_descargados = EDocument.objects.filter(
|
|
pedimento_id__in=pedimento_ids,
|
|
edocument_descargado=True
|
|
).count()
|
|
acuse_descargados = EDocument.objects.filter(
|
|
pedimento_id__in=pedimento_ids,
|
|
acuse_descargado=True
|
|
).count()
|
|
edocs_pendientes = edocs_total - edocs_descargados
|
|
acuses_pendientes = edocs_total - acuse_descargados
|
|
|
|
remesas_total = Document.objects.filter(
|
|
document_type__id=3,
|
|
pedimento_id__in=pedimento_ids
|
|
).count()
|
|
documentos_descargados = Document.objects.filter(
|
|
pedimento_id__in=pedimento_ids
|
|
).count()
|
|
|
|
partidas_total = Partida.objects.filter(pedimento_id__in=pedimento_ids).count()
|
|
partidas_descargadas = Partida.objects.filter(
|
|
pedimento_id__in=pedimento_ids,
|
|
descargado=True
|
|
).count()
|
|
partidas_pendientes = partidas_total - partidas_descargadas
|
|
|
|
# Indicadores de cumplimiento
|
|
cumplimiento_pedimentos = (pedimentos_completos / pedimentos_total * 100) if pedimentos_total else 0
|
|
cumplimiento_acuse_coves = (acuse_coves_procesados / coves_total * 100) if coves_total else 0
|
|
cumplimiento_coves = (coves_procesados / coves_total * 100) if coves_total else 0
|
|
cumplimiento_edocs = (edocs_descargados / edocs_total * 100) if edocs_total else 0
|
|
cumplimiento_acuses = (acuse_descargados / edocs_total * 100) if edocs_total else 0
|
|
cumplimiento_partidas = (partidas_descargadas / partidas_total * 100) if partidas_total else 0
|
|
|
|
return Response({
|
|
"pedimentos": {
|
|
"total": pedimentos_total,
|
|
"completos": pedimentos_completos,
|
|
"pendientes": pedimentos_pendientes,
|
|
"cumplimiento": round(cumplimiento_pedimentos, 2)
|
|
},
|
|
"coves": {
|
|
"total": coves_total,
|
|
"coves_procesados": coves_procesados,
|
|
"acuse_coves_procesados": acuse_coves_procesados,
|
|
"coves_pendientes": coves_pendientes,
|
|
"acuse_coves_pendientes": acuse_coves_pendientes,
|
|
"coves_cumplimiento": round(cumplimiento_coves, 2),
|
|
"acuse_coves_cumplimiento": round(cumplimiento_acuse_coves, 2)
|
|
},
|
|
"edocuments": {
|
|
"total": edocs_total,
|
|
"edocs_descargados": edocs_descargados,
|
|
"edocs_pendientes": edocs_pendientes,
|
|
"acuse_descargados": acuse_descargados,
|
|
"acuses_pendientes": acuses_pendientes,
|
|
"edocs_cumplimiento": round(cumplimiento_edocs, 2),
|
|
"acuses_cumplimiento": round(cumplimiento_acuses, 2)
|
|
},
|
|
"remesas": {
|
|
"total": remesas_total
|
|
},
|
|
"documentos": {
|
|
"descargados": documentos_descargados
|
|
},
|
|
"partidas": {
|
|
"total": partidas_total,
|
|
"partidas_descargadas": partidas_descargadas,
|
|
"partidas_pendientes": partidas_pendientes,
|
|
"cumplimiento": round(cumplimiento_partidas, 2)
|
|
}
|
|
}) |