diff --git a/.gitignore b/.gitignore index 0e69221..41074bc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ __pycache__/ # C extensions *.so +api/api_v2 main2.py sample.xml diff --git a/api/api_v2/__init__.py b/api/api_v2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/api.py b/api/api_v2/api.py new file mode 100644 index 0000000..0a49a1e --- /dev/null +++ b/api/api_v2/api.py @@ -0,0 +1,18 @@ +from fastapi import APIRouter +# En Python, no se pueden usar llaves {} para importar múltiples módulos. +# Debes usar paréntesis () para hacer importaciones multilínea. + +from api.api_v2.modules.acuses import router as acuses_router +from api.api_v2.modules.coves import router as coves_router +from api.api_v2.modules.edocs import router as edocs_router +from api.api_v2.modules.partidas import router as partidas_router +from api.api_v2.modules.pedimentos import router as pedimentos_router + +api_router = APIRouter() + +# Incluir routers de endpoints +api_router.include_router(acuses_router, tags=["acuses"]) +api_router.include_router(coves_router, tags=["coves"]) +api_router.include_router(edocs_router, tags=["edocs"]) +api_router.include_router(partidas_router, tags=["partidas"]) +api_router.include_router(pedimentos_router, tags=["pedimentos"]) diff --git a/api/api_v2/modules/__init__.py b/api/api_v2/modules/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/acuses/routers.py b/api/api_v2/modules/acuses/routers.py new file mode 100644 index 0000000..ce5096c --- /dev/null +++ b/api/api_v2/modules/acuses/routers.py @@ -0,0 +1,42 @@ +from fastapi import APIRouter, HTTPException +from fastapi.responses import JSONResponse +from typing import Dict, Any, List, Optional + +import asyncio +import logging +import traceback + +from .schemas import AcuseSchema, AcuseMasivoSchema +from .services import * + + +router = APIRouter(prefix="/acuses", tags=["Acuses"]) + +@router.post("/service/acuse/individual", response_model=Dict[str, Any]) +async def obtener_acuse(acuse_request: AcuseSchema): + """ + Endpoint para obtener el acuse de recibo de un documento específico. + """ + + pass + +@router.post("/service/acuse", response_model=Dict[str, Any]) +async def obtener_acuses(acuse_request: AcuseMasivoSchema): + """ + Endpoint para obtener acuses de recibo de documentos asociados a un pedimento. + """ + pass + +@router.post("/service/acuse_cove", response_model=Dict[str, Any]) +async def obtener_acuses_cove(acuse_request: AcuseMasivoSchema): + """ + Endpoint para obtener acuses de recibo de COVEs asociados a un pedimento. + """ + pass + +@router.post("/service/acuse_cove/individual", response_model=Dict[str, Any]) +async def obtener_acuse_cove(acuse_request: AcuseSchema): + """ + Endpoint para obtener el acuse de recibo de un COVE específico. + """ + pass \ No newline at end of file diff --git a/api/api_v2/modules/acuses/schemas.py b/api/api_v2/modules/acuses/schemas.py new file mode 100644 index 0000000..7708b1c --- /dev/null +++ b/api/api_v2/modules/acuses/schemas.py @@ -0,0 +1,22 @@ +from fastapi import FastAPI +from pydantic import BaseModel +from uuid import UUID + + +# Aplica para Acuse, Acuse Cove y Edocuments +class AcuseSchema(BaseModel): + pedimento: str + organizacion: str + numero_documento: str + vu_user: str + password: str + +class AcuseMasivoSchema(BaseModel): + pedimento: str + organizacion: str + numeros_documentos: list[str] + vu_user: str + password: str + + + diff --git a/api/api_v2/modules/acuses/services.py b/api/api_v2/modules/acuses/services.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/acuses/tasks.py b/api/api_v2/modules/acuses/tasks.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/acuses/tests.py b/api/api_v2/modules/acuses/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/coves/router.py b/api/api_v2/modules/coves/router.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/coves/schema.py b/api/api_v2/modules/coves/schema.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/coves/service.py b/api/api_v2/modules/coves/service.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/coves/tasks.py b/api/api_v2/modules/coves/tasks.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/coves/tests.py b/api/api_v2/modules/coves/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/edocs/router.py b/api/api_v2/modules/edocs/router.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/edocs/schema.py b/api/api_v2/modules/edocs/schema.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/edocs/service.py b/api/api_v2/modules/edocs/service.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/edocs/tasks.py b/api/api_v2/modules/edocs/tasks.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/edocs/tests.py b/api/api_v2/modules/edocs/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/partidas/router.py b/api/api_v2/modules/partidas/router.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/partidas/schema.py b/api/api_v2/modules/partidas/schema.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/partidas/service.py b/api/api_v2/modules/partidas/service.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/partidas/tasks.py b/api/api_v2/modules/partidas/tasks.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/partidas/tests.py b/api/api_v2/modules/partidas/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/pedimentos/router.py b/api/api_v2/modules/pedimentos/router.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/pedimentos/schema.py b/api/api_v2/modules/pedimentos/schema.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/pedimentos/service.py b/api/api_v2/modules/pedimentos/service.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/pedimentos/tasks.py b/api/api_v2/modules/pedimentos/tasks.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/pedimentos/tests.py b/api/api_v2/modules/pedimentos/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/tasks/routers.py b/api/api_v2/modules/tasks/routers.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/tasks/schemas.py b/api/api_v2/modules/tasks/schemas.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/tasks/services.py b/api/api_v2/modules/tasks/services.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/tasks/tasks.py b/api/api_v2/modules/tasks/tasks.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v2/modules/tasks/tests.py b/api/api_v2/modules/tasks/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/controllers/RESTController.py b/controllers/RESTController.py index 118c417..0a8ca9c 100644 --- a/controllers/RESTController.py +++ b/controllers/RESTController.py @@ -202,7 +202,7 @@ class APIController: return result except Exception as e: - print(f"Error al enviar documento: {e}") + print(f"Error al enviar documento: {document_data}, Error: {e}") # Limpiar archivo temporal en caso de error if 'temp_file_path' in locals() and os.path.exists(temp_file_path): os.unlink(temp_file_path) diff --git a/schemas/acuseSchema.py b/schemas/acuseSchema.py deleted file mode 100644 index fb8b0c0..0000000 --- a/schemas/acuseSchema.py +++ /dev/null @@ -1,8 +0,0 @@ -from fastapi import FastAPI -from pydantic import BaseModel -from uuid import UUID - -class AcuseSchema(BaseModel): - document_id: str - - diff --git a/schemas/remesaSchema.py b/schemas/remesaSchema.py new file mode 100644 index 0000000..ed9a4e3 --- /dev/null +++ b/schemas/remesaSchema.py @@ -0,0 +1,14 @@ +from fastapi import FastAPI +from pydantic import BaseModel +from uuid import UUID + + +# Aplica para Acuse y Acuse Cove +class Remesa(BaseModel): + numero_operacion: str + vu_user: str + password: str + + + + diff --git a/tasks.py b/tasks.py index de40d79..4ea6099 100644 --- a/tasks.py +++ b/tasks.py @@ -742,29 +742,81 @@ def coves_task(self, **kwargs): if not coves_response: raise Exception("Error en la petición REST para coves") + logger.info("[TASK] Petición REST para coves completada exitosamente") - # Subir documento de coves si la petición fue exitosa - try: - upload_result = await _post_coves( - response_service=service_data, - identificadores_cove=[coves_response[0].get('numero_cove')] if isinstance(coves_response, list) and coves_response else [] - ) - logger.info(f"Documento de coves subido exitosamente: {upload_result}") - except Exception as upload_err: - logger.error(f"Error al subir documento de coves: {upload_err}") - - self.update_state(state='PROGRESS', meta={'status': 'Finalizando proceso'}) + # Procesar acuses de documentos digitalizados + documentos_procesados = [] + documentos_exitosos = 0 + + coves = coves_response if isinstance(coves_response, list) else [] + logger.info(f"Procesando COVE para {len(coves)} documentos...") + + # Obtener credenciales VUCEM para la firma + credentials = await _get_vucem_credentials(service_data.get('pedimento', {}).get('contribuyente', ''), operation_name) + from controllers.SOAPController import soap_controller + + for idx, cove in enumerate(coves): + documento_info = { + "numero_cove": cove.get('numero_cove', 'N/A'), + "procesado": False, + "error": None + } + + if not cove.get('numero_cove'): + logger.warning(f"Documento {idx + 1} no tiene numero_cove, saltando...") + documento_info["error"] = "Sin número de cove" + documentos_procesados.append(documento_info) + continue + + try: + logger.info(f"Procesando cove para documento {idx + 1}: {cove['numero_cove']}") + soap_response = await get_soap_cove( + credenciales=credentials, + response_service=service_data, + soap_controller=soap_controller, + cove=cove, + idx=idx + 1 + ) + + if soap_response: + documento_info["procesado"] = True + documento_info["documento"] = soap_response.get('documento', {}) + documentos_exitosos += 1 + logger.info(f"cove del documento {idx + 1} procesado exitosamente") + else: + documento_info["error"] = "Error en petición SOAP" + logger.warning(f"No se pudo procesar el cove del documento {idx + 1}") + + except Exception as e: + logger.error(f"Error al procesar cove del documento {idx + 1}: {e}") + documento_info["error"] = str(e) + + documentos_procesados.append(documento_info) + + if documentos_exitosos == 0: + logger.error("No se pudo procesar ningún cove de documento digitalizado") + await _update_service_status(service_data['id'], ESTADO_ERROR, service_data, operation_name) + raise Exception("No se pudo procesar ningún acuse cove de documento digitalizado") + await _update_service_status(service_data['id'], ESTADO_FINALIZADO, service_data, operation_name) - + response_data = await _create_response( service_data=service_data, additional_data={ - "coves": coves_response + "covesDocs": documentos_procesados, + "total_documentos": len(coves), + "documentos_exitosos": documentos_exitosos, + "documentos_fallidos": len(coves) - documentos_exitosos }, - success_message="Coves obtenidos exitosamente" + success_message=f"Se procesaron {documentos_exitosos}/{len(coves)} cove de documentos exitosamente" ) - - logger.info(f"[TASK] Consulta de coves completada exitosamente - Servicio: {service_data['id']}") + + if documentos_exitosos < len(coves): + response_data["warnings"] = [ + f"Se procesaron solo {documentos_exitosos} de {len(coves)} coves" + ] + + logger.info(f"Procesamiento de acuses cove completado - Exitosos: {documentos_exitosos}/{len(coves)}") return response_data except Exception as e: diff --git a/utils/peticiones.py b/utils/peticiones.py index 26f13b1..89dd333 100644 --- a/utils/peticiones.py +++ b/utils/peticiones.py @@ -129,7 +129,6 @@ def extract_acuse_documento_from_soap(soap_response_text): # Testeado logger.error(f"Error extrayendo acuseDocumento: {e}") return None - def extract_pdf_bytes_from_xml(xml_path): """ Igual que extract_pdf_bytes_from_xml_content pero recibe una ruta de archivo. @@ -309,13 +308,11 @@ async def get_soap_pedimento_completo(credenciales, response_service, soap_contr organizacion=response_service['organizacion'], pedimento=response_service['pedimento']['id'], file_name=_file_name, - document_type=2, - + document_type=2, ) data['organizacion'] = response_service['organizacion'] data['id'] = response_service['pedimento']['id'] - data['fuente'] = 2 return { "servicio": response_service, @@ -970,6 +967,8 @@ async def get_soap_cove(credenciales, response_service, soap_controller, cove, i cove=cove['numero_cove'] ) + + ### >>> AQUÍ SE AÑADE EL LOGGER.DEBUG <<< ### logger.debug(f"XML SOAP generado: {soap_xml}") # 👈 Registra el XML completo @@ -989,6 +988,8 @@ async def get_soap_cove(credenciales, response_service, soap_controller, cove, i headers=soap_headers ) + + if (soap_response) and (not soap_error(soap_response)): logger.info(f"Petición SOAP exitosa - Status: {soap_response.status_code}") @@ -999,14 +1000,14 @@ async def get_soap_cove(credenciales, response_service, soap_controller, cove, i tipo_operacion = response_service['pedimento'].get('tipo_operacion', 'N/A') pedimento = response_service['pedimento'].get('pedimento', 'N/A') - _file_name = f"vu_COVE_{remesas}{no_partidas}{tipo_operacion}_{aduana}_{patente}_{pedimento}_{idx}.xml" + _file_name = f"vu_COVE_{remesas}{no_partidas}{tipo_operacion}_{aduana}_{patente}_{pedimento}_{cove['numero_cove']}.xml" document_response = await rest_controller.post_document( soap_response=soap_response, organizacion=response_service['organizacion'], pedimento=response_service['pedimento']['id'], file_name=_file_name, - document_type=8 + document_type=8, ) return {