feature/implementacion de gestor de informacion y archivos minIO
This commit is contained in:
@@ -61,6 +61,7 @@ except ImportError:
|
||||
|
||||
# Importar tarea de procesamiento de pedimento (Celery)
|
||||
from api.customs.tasks.microservice import procesar_pedimento_completo_individual
|
||||
from api.utils.storage_service import storage_service
|
||||
|
||||
def get_available_extractors():
|
||||
"""
|
||||
@@ -395,31 +396,8 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
|
||||
|
||||
@action(detail=False, methods=['post'], url_path='bulk-delete')
|
||||
def bulk_delete(self, request):
|
||||
"""
|
||||
Endpoint para eliminar múltiples pedimentos de manera masiva.
|
||||
import traceback
|
||||
|
||||
Payload esperado:
|
||||
{
|
||||
"ids": ["uuid1", "uuid2", "uuid3", ...]
|
||||
}
|
||||
|
||||
Respuesta exitosa:
|
||||
{
|
||||
"message": "Pedimentos eliminados exitosamente",
|
||||
"deleted_count": 3,
|
||||
"deleted_ids": ["uuid1", "uuid2", "uuid3"]
|
||||
}
|
||||
|
||||
Respuesta con errores:
|
||||
{
|
||||
"message": "Algunos pedimentos no pudieron ser eliminados",
|
||||
"deleted_count": 2,
|
||||
"deleted_ids": ["uuid1", "uuid2"],
|
||||
"failed_ids": ["uuid3"],
|
||||
"errors": ["No se encontró el pedimento con ID uuid3"]
|
||||
}
|
||||
"""
|
||||
# Obtener los IDs del payload
|
||||
ids = request.data.get('ids', [])
|
||||
|
||||
if not ids:
|
||||
@@ -434,18 +412,11 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
# Obtener el queryset filtrado por organización
|
||||
queryset = self.get_queryset()
|
||||
|
||||
# Filtrar solo los pedimentos que existen y pertenecen a la organización del usuario
|
||||
existing_pedimentos = queryset.filter(id__in=ids)
|
||||
existing_ids = list(existing_pedimentos.values_list('id', flat=True))
|
||||
|
||||
# Convertir UUIDs a strings para comparación
|
||||
existing_ids_str = [str(id) for id in existing_ids]
|
||||
requested_ids_str = [str(id) for id in ids]
|
||||
|
||||
# Identificar IDs que no existen o no pertenecen a la organización
|
||||
failed_ids = [id for id in requested_ids_str if id not in existing_ids_str]
|
||||
|
||||
deleted_count = 0
|
||||
@@ -453,20 +424,28 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
|
||||
|
||||
if existing_pedimentos.exists():
|
||||
try:
|
||||
# Eliminar los pedimentos encontrados
|
||||
for pedimento in existing_pedimentos:
|
||||
documentos = Document.objects.filter(pedimento_id=pedimento.id)
|
||||
for doc in documentos:
|
||||
if doc.archivo:
|
||||
ruta = str(doc.archivo)
|
||||
try:
|
||||
storage_service.delete_file(ruta)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
|
||||
documentos.delete()
|
||||
|
||||
deleted_count = existing_pedimentos.count()
|
||||
existing_pedimentos.delete()
|
||||
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return Response(
|
||||
{"error": f"Error al eliminar pedimentos: {str(e)}"},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
|
||||
# Agregar errores para IDs no encontrados
|
||||
if failed_ids:
|
||||
errors = [f"No se encontró el pedimento con ID {id} o no pertenece a su organización" for id in failed_ids]
|
||||
|
||||
# Preparar respuesta
|
||||
response_data = {
|
||||
"deleted_count": deleted_count,
|
||||
"deleted_ids": existing_ids_str
|
||||
@@ -793,14 +772,14 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
|
||||
print(f"Procesando documento: {file_name}")
|
||||
|
||||
try:
|
||||
# Leer el archivo desde el directorio temporal
|
||||
# Leer el archivo para extraer info del XML
|
||||
with open(file_path, 'rb') as f:
|
||||
file_content = f.read()
|
||||
from api.utils.helpers import extraer_info_pedimento_xml
|
||||
|
||||
# Extraer info del pedimento desde XML si es aplicable
|
||||
if file_name.lower().endswith('.xml'):
|
||||
try:
|
||||
from api.utils.helpers import extraer_info_pedimento_xml
|
||||
xml_info = extraer_info_pedimento_xml(file_content)
|
||||
if xml_info:
|
||||
if 'numero_operacion' in xml_info:
|
||||
@@ -812,11 +791,12 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
|
||||
print(f"Información extraída del XML: {xml_info}")
|
||||
except Exception as e:
|
||||
print(f"No se pudo extraer información del XML {file_name}: {str(e)}")
|
||||
|
||||
|
||||
# Obtener información del archivo
|
||||
extension = os.path.splitext(file_name)[1].lower().lstrip('.')
|
||||
file_size = os.path.getsize(file_path)
|
||||
|
||||
# Buscar si ya existe un documento con el mismo nombre para este pedimento
|
||||
# Buscar si ya existe un documento con el mismo nombre
|
||||
existing_documents = Document.objects.filter(
|
||||
pedimento_id=pedimento.id,
|
||||
organizacion=organizacion
|
||||
@@ -829,25 +809,30 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
|
||||
print(f"✅ Encontrado documento existente: ID {doc.id}")
|
||||
break
|
||||
|
||||
# Crear ContentFile
|
||||
django_file = ContentFile(file_content, name=file_name)
|
||||
|
||||
if existing_document:
|
||||
# Opcional: Eliminar el archivo físico anterior
|
||||
try:
|
||||
if existing_document.archivo and os.path.exists(existing_document.archivo.path):
|
||||
os.remove(existing_document.archivo.path)
|
||||
except (ValueError, OSError) as e:
|
||||
print(f"No se pudo eliminar archivo físico anterior: {str(e)}")
|
||||
# Eliminar archivo anterior si existe
|
||||
if existing_document.archivo:
|
||||
storage_service.delete_file(existing_document.archivo)
|
||||
|
||||
# Actualizar el documento existente
|
||||
existing_document.archivo = django_file
|
||||
existing_document.size = len(file_content)
|
||||
existing_document.extension = extension
|
||||
existing_document.updated_at = timezone.now() # Si tienes este campo
|
||||
existing_document.save()
|
||||
documents_created += 1
|
||||
print(f"📄 Documento actualizado: {file_name}")
|
||||
# Guardar nuevo archivo usando la ruta del archivo temporal
|
||||
ruta = storage_service.save_document_from_path(
|
||||
file_path=file_path,
|
||||
file_name=file_name,
|
||||
organizacion_id=organizacion.id,
|
||||
pedimento_app=pedimento_app,
|
||||
metadata={
|
||||
'pedimento_id': str(pedimento.id),
|
||||
'document_id': str(existing_document.id),
|
||||
'source': 'bulk_create_update'
|
||||
}
|
||||
)
|
||||
|
||||
if ruta:
|
||||
existing_document.archivo = ruta
|
||||
existing_document.size = file_size
|
||||
existing_document.extension = extension
|
||||
existing_document.save()
|
||||
documents_created += 1
|
||||
|
||||
else:
|
||||
# Crear nuevo documento
|
||||
@@ -856,16 +841,32 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
|
||||
pedimento_id=pedimento.id,
|
||||
document_type=document_type,
|
||||
fuente_id=4,
|
||||
archivo=django_file,
|
||||
size=len(file_content),
|
||||
size=file_size,
|
||||
extension=extension
|
||||
)
|
||||
documents_created += 1
|
||||
print(f"📄 Nuevo documento creado: {file_name}")
|
||||
|
||||
# Guardar archivo usando la ruta del archivo temporal
|
||||
ruta = storage_service.save_document_from_path(
|
||||
file_path=file_path,
|
||||
file_name=file_name,
|
||||
organizacion_id=organizacion.id,
|
||||
pedimento_app=pedimento_app,
|
||||
metadata={
|
||||
'pedimento_id': str(pedimento.id),
|
||||
'document_id': str(document.id),
|
||||
'source': 'bulk_create'
|
||||
}
|
||||
)
|
||||
|
||||
if ruta:
|
||||
document.archivo = ruta
|
||||
document.save()
|
||||
documents_created += 1
|
||||
else:
|
||||
document.delete()
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error al procesar documento {file_name}: {str(e)}")
|
||||
# Continuar con otros documentos
|
||||
|
||||
print(f"🏁 Procesamiento completado. Archivos procesados en este directorio.")
|
||||
|
||||
@@ -1359,8 +1360,7 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
|
||||
# print(f"🔄 Iniciando creación de documento para pedimento ID: {pedimento.id}")
|
||||
# Crear documento asociado al pedimento
|
||||
try:
|
||||
# print("📖 Leyendo archivo desde directorio temporal...")
|
||||
# Leer el archivo desde el directorio temporal
|
||||
# Leer el archivo desde el directorio temporal (solo para XML/nomenclatura especial)
|
||||
with open(file_path, 'rb') as f:
|
||||
file_content = f.read()
|
||||
|
||||
@@ -1372,54 +1372,66 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
|
||||
# Patrón: 7 dígitos, punto, 3 dígitos (ej: M8988852.300)
|
||||
patron_nomenclatura = re.compile(r'^[m|M]\d{7}\.\d{3}$', re.IGNORECASE)
|
||||
|
||||
# Separar nombre base y extensión
|
||||
nombre_base, extension = os.path.splitext(file_name)
|
||||
|
||||
if patron_nomenclatura.match(file_name_lower):
|
||||
tiene_nomenclatura_especial = True
|
||||
|
||||
# Procesar el archivo con el método auxiliar
|
||||
info_extraida = procesar_archivo_m_con_nomenclatura(file_content, pedimento )
|
||||
|
||||
# Procesar el archivo con el método auxiliar
|
||||
info_extraida = procesar_archivo_m_con_nomenclatura(file_content, pedimento)
|
||||
|
||||
if info_extraida.get('tiene_nomenclatura_especial', False):
|
||||
# Agregar información de procesamiento a los datos de respuesta
|
||||
if 'procesamiento_archivos' not in locals():
|
||||
procesamiento_archivos = []
|
||||
if 'procesamiento_archivos' not in locals():
|
||||
procesamiento_archivos = []
|
||||
procesamiento_archivos.append({
|
||||
'archivo': file_name,
|
||||
'nomenclatura_especial': True,
|
||||
'registros_encontrados': info_extraida.get('registros_encontrados', []),
|
||||
'actualizaciones': info_extraida.get('actualizaciones_aplicadas', [])
|
||||
})
|
||||
|
||||
procesamiento_archivos.append({
|
||||
'archivo': file_name,
|
||||
'nomenclatura_especial': True,
|
||||
'registros_encontrados': info_extraida.get('registros_encontrados', []),
|
||||
'actualizaciones': info_extraida.get('actualizaciones_aplicadas', [])
|
||||
})
|
||||
|
||||
# print(f"📄 Archivo leído: {len(file_content)} bytes")
|
||||
# Crear ContentFile que Django puede manejar correctamente
|
||||
django_file = ContentFile(file_content, name=file_name)
|
||||
extension = os.path.splitext(file_name)[1].lower().lstrip('.')
|
||||
file_size = os.path.getsize(file_path)
|
||||
|
||||
fuente, created = Fuente.objects.get_or_create(
|
||||
nombre="APP-EFC",
|
||||
descripcion='Transmitido por la app de escritorio'
|
||||
fuente, created = Fuente.objects.get_or_create(
|
||||
nombre="APP-EFC",
|
||||
descripcion='Transmitido por la app de escritorio'
|
||||
)
|
||||
|
||||
# print(f"Creando documento para archivo: {file_name}")
|
||||
# Crear documento - Django automáticamente guardará el archivo en media/documents/
|
||||
document = Document.objects.create(
|
||||
organizacion=organizacion,
|
||||
pedimento_id=pedimento.id,
|
||||
document_type=document_type,
|
||||
fuente_id=fuente.id,
|
||||
archivo=django_file,
|
||||
size=len(file_content),
|
||||
extension=os.path.splitext(file_name)[1].lower().lstrip('.')
|
||||
size=file_size,
|
||||
extension=extension
|
||||
)
|
||||
# print(f"Documento creado exitosamente: {document.id}")
|
||||
|
||||
documents_created += 1
|
||||
# print(f"📊 Total documentos creados hasta ahora: {documents_created}")
|
||||
ruta = storage_service.save_document_from_path(
|
||||
file_path=file_path,
|
||||
file_name=file_name,
|
||||
organizacion_id=organizacion.id,
|
||||
pedimento_app=pedimento_app,
|
||||
metadata={
|
||||
'pedimento_id': str(pedimento.id),
|
||||
'document_id': str(document.id),
|
||||
'source': 'efc_app_desk',
|
||||
'tiene_nomenclatura_especial': str(tiene_nomenclatura_especial)
|
||||
}
|
||||
)
|
||||
|
||||
if ruta:
|
||||
document.archivo = ruta
|
||||
document.save()
|
||||
documents_created += 1
|
||||
else:
|
||||
document.delete()
|
||||
archivo_original = folder_name + ('.zip' if any(f.endswith('.zip') for f in [a.name for a in archivos]) else '.rar')
|
||||
failed_files.append({
|
||||
"file": relative_path,
|
||||
"archivo_original": archivo_original,
|
||||
"error": "Error al guardar archivo en storage"
|
||||
})
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
# print(f"❌ Error al crear documento: {str(e)}")
|
||||
archivo_original = folder_name + ('.zip' if any(f.endswith('.zip') for f in [a.name for a in archivos]) else '.rar')
|
||||
failed_files.append({
|
||||
"file": relative_path,
|
||||
@@ -1817,32 +1829,18 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
|
||||
|
||||
# Crear documento asociado al pedimento
|
||||
try:
|
||||
# Leer el archivo desde el directorio temporal
|
||||
with open(file_path, 'rb') as f:
|
||||
file_content = f.read()
|
||||
extension = os.path.splitext(file_name)[1].lower().lstrip('.')
|
||||
file_size = os.path.getsize(file_path)
|
||||
|
||||
# Verificar si el archivo tiene la nomenclatura especial M8988852.300
|
||||
file_name_lower = file_name.lower()
|
||||
tiene_nomenclatura_especial = False
|
||||
info_extraida = {}
|
||||
|
||||
# Patrón: 7 dígitos, punto, 3 dígitos (ej: M8988852.300)
|
||||
patron_nomenclatura = re.compile(r'^[m|M]\d{7}\.\d{3}$', re.IGNORECASE)
|
||||
|
||||
# Separar nombre base y extensión
|
||||
nombre_base, extension = os.path.splitext(file_name)
|
||||
|
||||
if patron_nomenclatura.match(file_name_lower):
|
||||
tiene_nomenclatura_especial = True
|
||||
|
||||
# Procesar el archivo con el método auxiliar
|
||||
with open(file_path, 'rb') as f:
|
||||
file_content = f.read()
|
||||
info_extraida = procesar_archivo_m_con_nomenclatura(file_content, pedimento)
|
||||
|
||||
if info_extraida.get('tiene_nomenclatura_especial', False):
|
||||
# Agregar información de procesamiento a los datos de respuesta
|
||||
if 'procesamiento_archivos' not in locals():
|
||||
procesamiento_archivos = []
|
||||
|
||||
procesamiento_archivos.append({
|
||||
'archivo': file_name,
|
||||
'nomenclatura_especial': True,
|
||||
@@ -1850,20 +1848,15 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
|
||||
'actualizaciones': info_extraida.get('actualizaciones_aplicadas', [])
|
||||
})
|
||||
|
||||
# Crear ContentFile que Django puede manejar correctamente
|
||||
django_file = ContentFile(file_content, name=file_name)
|
||||
|
||||
fuente, created = Fuente.objects.get_or_create(
|
||||
fuente, _ = Fuente.objects.get_or_create(
|
||||
nombre="APP-EFC",
|
||||
descripcion='Transmitido por la app de escritorio'
|
||||
)
|
||||
|
||||
# Buscar si ya existe un documento con el mismo nombre para este pedimento
|
||||
existing_documents = Document.objects.filter(
|
||||
pedimento_id=pedimento.id,
|
||||
organizacion=organizacion
|
||||
)
|
||||
|
||||
existing_document = None
|
||||
for doc in existing_documents:
|
||||
if is_same_document(doc, file_name):
|
||||
@@ -1871,37 +1864,49 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
|
||||
break
|
||||
|
||||
if existing_document:
|
||||
# Opcional: Eliminar el archivo físico anterior
|
||||
try:
|
||||
if existing_document.archivo and os.path.exists(existing_document.archivo.path):
|
||||
os.remove(existing_document.archivo.path)
|
||||
except (ValueError, OSError) as e:
|
||||
pass
|
||||
|
||||
# Actualizar el documento existente con el nuevo archivo y datos
|
||||
existing_document.archivo = django_file
|
||||
existing_document.size = len(file_content)
|
||||
existing_document.extension = extension
|
||||
existing_document.updated_at = timezone.now() # Si tienes este campo
|
||||
existing_document.save()
|
||||
|
||||
documents_created += 1
|
||||
|
||||
if existing_document.archivo:
|
||||
storage_service.delete_file(existing_document.archivo)
|
||||
|
||||
ruta = storage_service.save_document_from_path(
|
||||
file_path=file_path,
|
||||
file_name=file_name,
|
||||
organizacion_id=organizacion.id,
|
||||
pedimento_app=pedimento_app
|
||||
)
|
||||
|
||||
if ruta:
|
||||
existing_document.archivo = ruta
|
||||
existing_document.size = file_size
|
||||
existing_document.extension = extension
|
||||
existing_document.save()
|
||||
documents_created += 1
|
||||
else:
|
||||
# Crear documento - Django automáticamente guardará el archivo en media/documents/
|
||||
document = Document.objects.create(
|
||||
organizacion=organizacion,
|
||||
pedimento_id=pedimento.id,
|
||||
document_type=document_type,
|
||||
fuente_id=fuente.id,
|
||||
archivo=django_file,
|
||||
size=len(file_content),
|
||||
extension=os.path.splitext(file_name)[1].lower().lstrip('.')
|
||||
size=file_size,
|
||||
extension=extension
|
||||
)
|
||||
documents_created += 1
|
||||
|
||||
ruta = storage_service.save_document_from_path(
|
||||
file_path=file_path,
|
||||
file_name=file_name,
|
||||
organizacion_id=organizacion.id,
|
||||
pedimento_app=pedimento_app
|
||||
)
|
||||
|
||||
if ruta:
|
||||
document.archivo = ruta
|
||||
document.save()
|
||||
documents_created += 1
|
||||
else:
|
||||
document.delete()
|
||||
raise Exception("Error al guardar archivo")
|
||||
|
||||
except Exception as e:
|
||||
archivo_original = folder_name + ('.zip' if any(f.endswith('.zip') for f in [a.name for a in archivos]) else '.rar')
|
||||
archivo_original = folder_name + '.zip'
|
||||
failed_records.append({
|
||||
"file": relative_path,
|
||||
"archivo_original": archivo_original,
|
||||
|
||||
Reference in New Issue
Block a user