Reportes
This commit is contained in:
30
api/reports/migrations/0001_initial.py
Normal file
30
api/reports/migrations/0001_initial.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Generated by Django 5.2.3 on 2025-10-21 23:56
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ReportDocument',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('filters', models.JSONField(blank=True, null=True)),
|
||||||
|
('status', models.CharField(choices=[('pending', 'Pendiente'), ('processing', 'Procesando'), ('ready', 'Listo'), ('error', 'Error')], default='pending', max_length=20)),
|
||||||
|
('file', models.FileField(blank=True, null=True, upload_to='reports/')),
|
||||||
|
('error_message', models.TextField(blank=True, null=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('finished_at', models.DateTimeField(blank=True, null=True)),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='report_documents', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
0
api/reports/migrations/__init__.py
Normal file
0
api/reports/migrations/__init__.py
Normal file
@@ -1,3 +1,21 @@
|
|||||||
from django.db import models
|
|
||||||
|
|
||||||
# Create your models here.
|
from django.db import models
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
class ReportDocument(models.Model):
|
||||||
|
STATUS_CHOICES = [
|
||||||
|
('pending', 'Pendiente'),
|
||||||
|
('processing', 'Procesando'),
|
||||||
|
('ready', 'Listo'),
|
||||||
|
('error', 'Error'),
|
||||||
|
]
|
||||||
|
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='report_documents')
|
||||||
|
filters = models.JSONField(blank=True, null=True)
|
||||||
|
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
|
||||||
|
file = models.FileField(upload_to='reports/', blank=True, null=True)
|
||||||
|
error_message = models.TextField(blank=True, null=True)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
finished_at = models.DateTimeField(blank=True, null=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Reporte {self.id} - {self.status}"
|
||||||
|
|||||||
85
api/reports/tasks/report_document.py
Normal file
85
api/reports/tasks/report_document.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
from celery import shared_task
|
||||||
|
from django.core.files.base import ContentFile
|
||||||
|
from django.utils import timezone
|
||||||
|
from api.reports.models import ReportDocument
|
||||||
|
from api.customs.models import Pedimento, Cove, EDocument, Partida
|
||||||
|
from django.db.models import Q
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def generate_report_document(report_id):
|
||||||
|
try:
|
||||||
|
report = ReportDocument.objects.get(id=report_id)
|
||||||
|
report.status = 'processing'
|
||||||
|
report.save(update_fields=['status'])
|
||||||
|
filters = report.filters or {}
|
||||||
|
# Construir Q para filtros complejos
|
||||||
|
pedimentos_filters = Q()
|
||||||
|
if filters.get('organizacion_id'):
|
||||||
|
pedimentos_filters &= Q(organizacion_id=filters['organizacion_id'])
|
||||||
|
if filters.get('fecha_pago__gte'):
|
||||||
|
pedimentos_filters &= Q(fecha_pago__gte=filters['fecha_pago__gte'])
|
||||||
|
if filters.get('fecha_pago__lte'):
|
||||||
|
pedimentos_filters &= Q(fecha_pago__lte=filters['fecha_pago__lte'])
|
||||||
|
if filters.get('contribuyente__rfc'):
|
||||||
|
pedimentos_filters &= Q(contribuyente__rfc=filters['contribuyente__rfc'])
|
||||||
|
if filters.get('patente'):
|
||||||
|
pedimentos_filters &= Q(patente=filters['patente'])
|
||||||
|
if filters.get('aduana'):
|
||||||
|
pedimentos_filters &= Q(aduana=filters['aduana'])
|
||||||
|
if filters.get('pedimento'):
|
||||||
|
pedimentos_filters &= Q(pedimento=filters['pedimento'])
|
||||||
|
if filters.get('pedimento_app'):
|
||||||
|
pedimentos_filters &= Q(pedimento_app=filters['pedimento_app'])
|
||||||
|
if filters.get('regimen'):
|
||||||
|
pedimentos_filters &= Q(regimen=filters['regimen'])
|
||||||
|
if filters.get('tipo_operacion'):
|
||||||
|
pedimentos_filters &= Q(tipo_operacion_id=filters['tipo_operacion'])
|
||||||
|
# Consulta asíncrona de los modelos
|
||||||
|
pedimentos = Pedimento.objects.filter(pedimentos_filters)
|
||||||
|
filename = filters.get('filename')
|
||||||
|
if filename:
|
||||||
|
filename = f"{filename}.csv" if not filename.endswith('.csv') else filename
|
||||||
|
else:
|
||||||
|
filename = f"report_{report.id}_{timezone.now().strftime('%Y%m%d%H%M%S')}.csv"
|
||||||
|
file_path = os.path.join(settings.MEDIA_ROOT, 'reports', filename)
|
||||||
|
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||||
|
with open(file_path, 'w', newline='', encoding='utf-8') as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
headers = [
|
||||||
|
'aduana', 'patente', 'regimen', 'pedimento', 'pedimento_app', 'clave_pedimento',
|
||||||
|
'tipo_operacion_id', 'contribuyente_id', 'tipo_documento', 'numero_documento', 'estado', 'acuse_estado'
|
||||||
|
]
|
||||||
|
writer.writerow(headers)
|
||||||
|
for ped in pedimentos:
|
||||||
|
for cove in Cove.objects.filter(pedimento=ped):
|
||||||
|
writer.writerow([
|
||||||
|
ped.aduana, ped.patente, ped.regimen, ped.pedimento, ped.pedimento_app,
|
||||||
|
ped.clave_pedimento, ped.tipo_operacion_id, ped.contribuyente_id,
|
||||||
|
'COVE', cove.numero_cove, cove.cove_descargado, cove.acuse_cove_descargado
|
||||||
|
])
|
||||||
|
for edoc in EDocument.objects.filter(pedimento=ped):
|
||||||
|
writer.writerow([
|
||||||
|
ped.aduana, ped.patente, ped.regimen, ped.pedimento, ped.pedimento_app,
|
||||||
|
ped.clave_pedimento, ped.tipo_operacion_id, ped.contribuyente_id,
|
||||||
|
'EDOC', edoc.numero_edocument, edoc.edocument_descargado, edoc.acuse_descargado
|
||||||
|
])
|
||||||
|
for partida in Partida.objects.filter(pedimento=ped):
|
||||||
|
writer.writerow([
|
||||||
|
ped.aduana, ped.patente, ped.regimen, ped.pedimento, ped.pedimento_app,
|
||||||
|
ped.clave_pedimento, ped.tipo_operacion_id, ped.contribuyente_id,
|
||||||
|
'PARTIDA', partida.numero_partida, partida.descargado, ''
|
||||||
|
])
|
||||||
|
# Guardar el archivo en el modelo
|
||||||
|
with open(file_path, 'rb') as f:
|
||||||
|
report.file.save(filename, ContentFile(f.read()), save=True)
|
||||||
|
report.status = 'ready'
|
||||||
|
report.finished_at = timezone.now()
|
||||||
|
report.save(update_fields=['status', 'file', 'finished_at'])
|
||||||
|
except Exception as e:
|
||||||
|
report.status = 'error'
|
||||||
|
report.error_message = str(e)
|
||||||
|
report.finished_at = timezone.now()
|
||||||
|
report.save(update_fields=['status', 'error_message', 'finished_at'])
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from .views import ExportModelView, dashboard_summary
|
from .views import ExportModelView, dashboard_summary
|
||||||
# from .views_stats import documentos_por_fecha
|
# from .views_stats import documentos_por_fecha
|
||||||
from .views_table import table_summary
|
from .views_table import table_summary, report_document_status, report_document_list, report_document_download
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('exportmodel/', ExportModelView.as_view(), name='export-model'),
|
path('exportmodel/', ExportModelView.as_view(), name='export-model'),
|
||||||
path('dashboard/summary/', dashboard_summary, name='dashboard-summary'),
|
path('dashboard/summary/', dashboard_summary, name='dashboard-summary'),
|
||||||
#path('documentos-por-fecha/', documentos_por_fecha, name='documentos-por-fecha'),
|
#path('documentos-por-fecha/', documentos_por_fecha, name='documentos-por-fecha'),
|
||||||
path('table-summary/', table_summary, name='table-summary'),
|
path('table-summary/', table_summary, name='table-summary'),
|
||||||
|
path('report-document-status/<int:report_id>/', report_document_status, name='report_document_status'),
|
||||||
|
path('report-document-list/', report_document_list, name='report_document_list'),
|
||||||
|
path('report-document-download/<int:report_id>/', report_document_download, name='report_document_download'),
|
||||||
]
|
]
|
||||||
@@ -1,34 +1,19 @@
|
|||||||
|
from api.reports.models import ReportDocument
|
||||||
|
from api.reports.tasks.report_document import generate_report_document
|
||||||
|
from django.http import FileResponse
|
||||||
from rest_framework.decorators import api_view, permission_classes
|
from rest_framework.decorators import api_view, permission_classes
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.pagination import PageNumberPagination
|
|
||||||
from django.db.models import Value, CharField, Q, Exists, OuterRef, Subquery
|
|
||||||
from django.db.models.functions import Cast
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from api.customs.models import Pedimento, Cove, EDocument, Partida
|
|
||||||
|
|
||||||
class CustomPagination(PageNumberPagination):
|
|
||||||
page_size = 50
|
|
||||||
page_size_query_param = 'page_size'
|
|
||||||
max_page_size = 1000
|
|
||||||
|
|
||||||
@api_view(['GET'])
|
@api_view(['GET'])
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
def table_summary(request):
|
def table_summary(request):
|
||||||
# ...existing code...
|
|
||||||
|
|
||||||
# Si se solicita CSV, generar archivo y devolverlo (después de definir pedimentos_filters)
|
|
||||||
|
|
||||||
|
|
||||||
my_tags = ['Reportes']
|
|
||||||
"""
|
"""
|
||||||
Endpoint que devuelve un resumen tabulado de pedimentos y sus documentos asociados.
|
Solo dispara la tarea asíncrona para generar el reporte CSV. No consulta ni procesa datos.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
org_id = request.query_params.get('organizacion_id')
|
org_id = request.query_params.get('organizacion_id')
|
||||||
if not org_id:
|
if not org_id:
|
||||||
return Response({"error": "organizacion_id es requerido"}, status=400)
|
return Response({"error": "organizacion_id es requerido"}, status=400)
|
||||||
|
|
||||||
# Obtener filtros de query params
|
# Obtener filtros de query params
|
||||||
tipo_documento = request.query_params.get('tipo_documento')
|
tipo_documento = request.query_params.get('tipo_documento')
|
||||||
rfc = request.query_params.get('contribuyente__rfc')
|
rfc = request.query_params.get('contribuyente__rfc')
|
||||||
@@ -40,162 +25,18 @@ def table_summary(request):
|
|||||||
pedimento_app = request.query_params.get('pedimento_app')
|
pedimento_app = request.query_params.get('pedimento_app')
|
||||||
regimen = request.query_params.get('regimen')
|
regimen = request.query_params.get('regimen')
|
||||||
tipo_operacion = request.query_params.get('tipo_operacion')
|
tipo_operacion = request.query_params.get('tipo_operacion')
|
||||||
|
|
||||||
# Si no se proporcionan fechas, establecer un rango por defecto de los últimos 30 días
|
# Si no se proporcionan fechas, establecer un rango por defecto de los últimos 30 días
|
||||||
if not fecha_pago_gte and not fecha_pago_lte:
|
if not fecha_pago_gte and not fecha_pago_lte:
|
||||||
|
from datetime import datetime, timedelta
|
||||||
fecha_pago_lte = datetime.now().date()
|
fecha_pago_lte = datetime.now().date()
|
||||||
fecha_pago_gte = fecha_pago_lte - timedelta(days=30)
|
fecha_pago_gte = fecha_pago_lte - timedelta(days=30)
|
||||||
|
# Crear el registro y lanzar la tarea Celery
|
||||||
# Construir filtros base para pedimentos
|
filename_param = request.query_params.get('filename')
|
||||||
pedimentos_filters = Q()
|
if filename_param:
|
||||||
pedimentos_filters &= Q(organizacion_id=org_id)
|
filename = filename_param
|
||||||
if fecha_pago_gte:
|
|
||||||
pedimentos_filters &= Q(fecha_pago__gte=fecha_pago_gte)
|
|
||||||
if fecha_pago_lte:
|
|
||||||
pedimentos_filters &= Q(fecha_pago__lte=fecha_pago_lte)
|
|
||||||
if rfc:
|
|
||||||
pedimentos_filters &= Q(contribuyente__rfc=rfc)
|
|
||||||
if patente:
|
|
||||||
pedimentos_filters &= Q(patente=patente)
|
|
||||||
if aduana:
|
|
||||||
pedimentos_filters &= Q(aduana=aduana)
|
|
||||||
if pedimento:
|
|
||||||
pedimentos_filters &= Q(pedimento=pedimento)
|
|
||||||
if pedimento_app:
|
|
||||||
pedimentos_filters &= Q(pedimento_app=pedimento_app)
|
|
||||||
if regimen:
|
|
||||||
pedimentos_filters &= Q(regimen=regimen)
|
|
||||||
if tipo_operacion:
|
|
||||||
pedimentos_filters &= Q(tipo_operacion_id=tipo_operacion)
|
|
||||||
|
|
||||||
# Si se solicita los últimos 100 registros actualizados
|
|
||||||
if request.query_params.get('ultimos') == '1':
|
|
||||||
pedimentos = Pedimento.objects.filter(pedimentos_filters).order_by('-updated_at')[:100]
|
|
||||||
else:
|
else:
|
||||||
pedimentos = Pedimento.objects.filter(pedimentos_filters)
|
filename = None
|
||||||
|
filtros = {
|
||||||
# Serializar pedimentos con documentos relacionados
|
|
||||||
results = []
|
|
||||||
for ped in pedimentos:
|
|
||||||
ped_dict = {
|
|
||||||
'aduana': ped.aduana,
|
|
||||||
'patente': ped.patente,
|
|
||||||
'regimen': ped.regimen,
|
|
||||||
'pedimento': ped.pedimento,
|
|
||||||
'pedimento_app': ped.pedimento_app,
|
|
||||||
'clave_pedimento': ped.clave_pedimento,
|
|
||||||
'tipo_operacion_id': ped.tipo_operacion_id,
|
|
||||||
'contribuyente_id': ped.contribuyente_id,
|
|
||||||
'documentos': []
|
|
||||||
}
|
|
||||||
# COVEs
|
|
||||||
for cove in Cove.objects.filter(pedimento=ped):
|
|
||||||
ped_dict['documentos'].append({
|
|
||||||
'tipo': 'COVE',
|
|
||||||
'numero': cove.numero_cove,
|
|
||||||
'estado': cove.cove_descargado,
|
|
||||||
'acuse_estado': cove.acuse_cove_descargado
|
|
||||||
})
|
|
||||||
# EDOCs
|
|
||||||
for edoc in EDocument.objects.filter(pedimento=ped):
|
|
||||||
ped_dict['documentos'].append({
|
|
||||||
'tipo': 'EDOC',
|
|
||||||
'numero': edoc.numero_edocument,
|
|
||||||
'estado': edoc.edocument_descargado,
|
|
||||||
'acuse_estado': edoc.acuse_descargado,
|
|
||||||
})
|
|
||||||
# PARTIDAS
|
|
||||||
for partida in Partida.objects.filter(pedimento=ped):
|
|
||||||
ped_dict['documentos'].append({
|
|
||||||
'tipo': 'PARTIDA',
|
|
||||||
'numero': partida.numero_partida,
|
|
||||||
'estado': partida.descargado
|
|
||||||
})
|
|
||||||
results.append(ped_dict)
|
|
||||||
|
|
||||||
if request.query_params.get('csv') == '1':
|
|
||||||
import csv
|
|
||||||
from django.http import HttpResponse
|
|
||||||
headers = [
|
|
||||||
'aduana', 'patente', 'regimen', 'pedimento', 'pedimento_app', 'clave_pedimento',
|
|
||||||
'tipo_operacion_id', 'contribuyente_id', 'tipo_documento', 'numero_documento', 'estado', 'acuse_estado'
|
|
||||||
]
|
|
||||||
response = HttpResponse(content_type='text/csv')
|
|
||||||
response['Content-Disposition'] = 'attachment; filename=table_summary.csv'
|
|
||||||
writer = csv.writer(response)
|
|
||||||
writer.writerow(headers)
|
|
||||||
# Llenar filas
|
|
||||||
if request.query_params.get('ultimos') == '1':
|
|
||||||
pedimentos = Pedimento.objects.filter(pedimentos_filters).order_by('-updated_at')[:100]
|
|
||||||
else:
|
|
||||||
pedimentos = Pedimento.objects.filter(pedimentos_filters)
|
|
||||||
for ped in pedimentos:
|
|
||||||
# COVEs
|
|
||||||
for cove in Cove.objects.filter(pedimento=ped):
|
|
||||||
writer.writerow([
|
|
||||||
ped.aduana, ped.patente, ped.regimen, ped.pedimento, ped.pedimento_app,
|
|
||||||
ped.clave_pedimento, ped.tipo_operacion_id, ped.contribuyente_id,
|
|
||||||
'COVE', cove.numero_cove, cove.cove_descargado, cove.acuse_cove_descargado
|
|
||||||
])
|
|
||||||
# EDOCs
|
|
||||||
for edoc in EDocument.objects.filter(pedimento=ped):
|
|
||||||
writer.writerow([
|
|
||||||
ped.aduana, ped.patente, ped.regimen, ped.pedimento, ped.pedimento_app,
|
|
||||||
ped.clave_pedimento, ped.tipo_operacion_id, ped.contribuyente_id,
|
|
||||||
'EDOC', edoc.numero_edocument, edoc.edocument_descargado, edoc.acuse_descargado
|
|
||||||
])
|
|
||||||
# PARTIDAS
|
|
||||||
for partida in Partida.objects.filter(pedimento=ped):
|
|
||||||
writer.writerow([
|
|
||||||
ped.aduana, ped.patente, ped.regimen, ped.pedimento, ped.pedimento_app,
|
|
||||||
ped.clave_pedimento, ped.tipo_operacion_id, ped.contribuyente_id,
|
|
||||||
'PARTIDA', partida.numero_partida, partida.descargado, ''
|
|
||||||
])
|
|
||||||
return response
|
|
||||||
# Si se solicita Excel, generar archivo y devolverlo
|
|
||||||
if request.query_params.get('excel') == '1':
|
|
||||||
import openpyxl
|
|
||||||
from openpyxl.utils import get_column_letter
|
|
||||||
from django.http import HttpResponse
|
|
||||||
wb = openpyxl.Workbook()
|
|
||||||
ws = wb.active
|
|
||||||
ws.title = "Resumen"
|
|
||||||
# Encabezados
|
|
||||||
headers = [
|
|
||||||
'aduana', 'patente', 'regimen', 'pedimento', 'pedimento_app', 'clave_pedimento',
|
|
||||||
'tipo_operacion_id', 'contribuyente_id', 'tipo_documento', 'numero_documento', 'estado', 'acuse_estado'
|
|
||||||
]
|
|
||||||
ws.append(headers)
|
|
||||||
# Llenar filas
|
|
||||||
for ped in results:
|
|
||||||
for doc in ped['documentos']:
|
|
||||||
ws.append([
|
|
||||||
ped['aduana'], ped['patente'], ped['regimen'], ped['pedimento'], ped['pedimento_app'],
|
|
||||||
ped['clave_pedimento'], ped['tipo_operacion_id'], ped['contribuyente_id'],
|
|
||||||
doc.get('tipo'), doc.get('numero'), doc.get('estado'), doc.get('acuse_estado')
|
|
||||||
])
|
|
||||||
# Ajustar ancho de columnas
|
|
||||||
for i, col in enumerate(headers, 1):
|
|
||||||
ws.column_dimensions[get_column_letter(i)].width = max(12, len(col) + 2)
|
|
||||||
# Guardar en memoria y devolver como respuesta
|
|
||||||
from io import BytesIO
|
|
||||||
output = BytesIO()
|
|
||||||
wb.save(output)
|
|
||||||
output.seek(0)
|
|
||||||
response = HttpResponse(
|
|
||||||
output.read(),
|
|
||||||
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
||||||
)
|
|
||||||
response['Content-Disposition'] = 'attachment; filename=table_summary.xlsx'
|
|
||||||
return response
|
|
||||||
|
|
||||||
# Aplicar paginación manual sobre results
|
|
||||||
paginator = CustomPagination()
|
|
||||||
page = paginator.paginate_queryset(results, request)
|
|
||||||
|
|
||||||
return paginator.get_paginated_response({
|
|
||||||
"results": page,
|
|
||||||
"filtros_aplicados": {
|
|
||||||
"organizacion_id": org_id,
|
"organizacion_id": org_id,
|
||||||
"tipo_documento": tipo_documento,
|
"tipo_documento": tipo_documento,
|
||||||
"contribuyente__rfc": rfc,
|
"contribuyente__rfc": rfc,
|
||||||
@@ -206,6 +47,64 @@ def table_summary(request):
|
|||||||
"pedimento": pedimento,
|
"pedimento": pedimento,
|
||||||
"pedimento_app": pedimento_app,
|
"pedimento_app": pedimento_app,
|
||||||
"regimen": regimen,
|
"regimen": regimen,
|
||||||
"tipo_operacion": tipo_operacion
|
"tipo_operacion": tipo_operacion,
|
||||||
|
"filename": filename
|
||||||
}
|
}
|
||||||
})
|
report = ReportDocument.objects.create(
|
||||||
|
user=request.user,
|
||||||
|
filters=filtros,
|
||||||
|
status='pending'
|
||||||
|
)
|
||||||
|
generate_report_document.delay(report.id)
|
||||||
|
return Response({
|
||||||
|
"report_id": report.id,
|
||||||
|
"status": report.status,
|
||||||
|
"created_at": report.created_at,
|
||||||
|
"download_url": report.file.url if report.file else None
|
||||||
|
}, status=202)
|
||||||
|
|
||||||
|
@api_view(['GET'])
|
||||||
|
@permission_classes([IsAuthenticated])
|
||||||
|
def report_document_status(request, report_id):
|
||||||
|
try:
|
||||||
|
report = ReportDocument.objects.get(id=report_id, user=request.user)
|
||||||
|
data = {
|
||||||
|
"report_id": report.id,
|
||||||
|
"status": report.status,
|
||||||
|
"created_at": report.created_at,
|
||||||
|
"finished_at": report.finished_at,
|
||||||
|
"error_message": report.error_message,
|
||||||
|
"download_url": report.file.url if report.file else None
|
||||||
|
}
|
||||||
|
return Response(data)
|
||||||
|
except ReportDocument.DoesNotExist:
|
||||||
|
return Response({"error": "Reporte no encontrado"}, status=404)
|
||||||
|
|
||||||
|
@api_view(['GET'])
|
||||||
|
@permission_classes([IsAuthenticated])
|
||||||
|
def report_document_list(request):
|
||||||
|
reports = ReportDocument.objects.filter(user=request.user).order_by('-created_at')
|
||||||
|
data = [
|
||||||
|
{
|
||||||
|
"report_id": r.id,
|
||||||
|
"status": r.status,
|
||||||
|
"created_at": r.created_at,
|
||||||
|
"finished_at": r.finished_at,
|
||||||
|
"error_message": r.error_message,
|
||||||
|
"download_url": r.file.url if r.file else None
|
||||||
|
}
|
||||||
|
for r in reports
|
||||||
|
]
|
||||||
|
return Response(data)
|
||||||
|
|
||||||
|
@api_view(['GET'])
|
||||||
|
@permission_classes([IsAuthenticated])
|
||||||
|
def report_document_download(request, report_id):
|
||||||
|
try:
|
||||||
|
report = ReportDocument.objects.get(id=report_id, user=request.user)
|
||||||
|
if not report.file:
|
||||||
|
return Response({"error": "El archivo aún no está disponible"}, status=404)
|
||||||
|
response = FileResponse(report.file.open('rb'), as_attachment=True, filename=report.file.name)
|
||||||
|
return response
|
||||||
|
except ReportDocument.DoesNotExist:
|
||||||
|
return Response({"error": "Reporte no encontrado"}, status=404)
|
||||||
@@ -266,7 +266,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
LANGUAGE_CODE = 'en-us'
|
LANGUAGE_CODE = 'en-us'
|
||||||
TIME_ZONE = 'America/Ojinaga' # Zona horaria de Cd. Juárez, Chihuahua
|
TIME_ZONE = 'America/Mexico_City' # Zona horaria de Cd. Juárez, Chihuahua
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user