feature/pedimento completo carga remesas, coves, edocs y acuses y aparte descarga sus documentos, se corrigieron las formulas de remesas, acuse y e documents para permitir la correcta descarga de susdocumentos y se aseguro que el status sea el correcto #8

Merged
jcedilloAS merged 1 commits from feature/T2026-05-016-y-T2026-05-030 into main 2026-05-18 18:04:36 +00:00
13 changed files with 292 additions and 131 deletions
Showing only changes of commit f96e5a227b - Show all commits

View File

@@ -33,19 +33,20 @@ async def obtener_acuse(**kwargs):
file_name_request = f"vu_AC_{pedimento_app}_{idEdocument_efc}_REQUEST.xml" file_name_request = f"vu_AC_{pedimento_app}_{idEdocument_efc}_REQUEST.xml"
document_response = await acuse_rest_controller.post_document( document_response = await acuse_rest_controller.post_or_update_document(
soap_response=soap_xml, soap_response=soap_xml,
organizacion=organizacion_efc, organizacion=organizacion_efc,
pedimento=pedimento_id_efc, pedimento=pedimento_id_efc,
file_name=file_name_request, file_name=file_name_request,
document_type=25, # Tipo de documento para request de acuse VU document_type=25,
identifier=idEdocument_efc,
) )
except Exception as e: except Exception as e:
logger.error(f"Error al enviar solicitud SOAP: {e}") logger.error(f"Error al enviar solicitud SOAP: {e}")
response = await acuse_vu_controller.make_request_async( response = await acuse_vu_controller.make_request_async(
"ventanilla-acuses-HA/ConsultaAcusesServiceWS?wsdl", "ventanilla-acuses-HA/ConsultaAcusesServiceWS?wsdl",
data=soap_xml, data=soap_xml,
headers=soap_headers headers=soap_headers
) )
@@ -72,11 +73,32 @@ async def obtener_acuse(**kwargs):
if soap_error(response): if soap_error(response):
logger.error("Error SOAP detectado en la respuesta") logger.error("Error SOAP detectado en la respuesta")
pedimento_efc = kwargs.get('pedimento', {})
organizacion_efc = pedimento_efc.get("organizacion", None)
pedimento_id_efc = pedimento_efc.get("id", None)
pedimento_app = pedimento_efc.get('pedimento_app', 'N/A')
idEdocument_efc = kwargs['edoc'].get('numero_edocument', 'N/A')
file_name_response = f"vu_AC_{pedimento_app}_{idEdocument_efc}_RESPONSE_ERROR.xml"
logger.info(f"Guardando RESPONSE_ERROR: file={file_name_response}, organizacion={organizacion_efc}, pedimento={pedimento_id_efc}")
doc_result = await acuse_rest_controller.post_or_update_document(
soap_response=response.text,
organizacion=organizacion_efc,
pedimento=pedimento_id_efc,
file_name=file_name_response,
document_type=26,
identifier=idEdocument_efc,
)
if doc_result is None:
logger.error(f"post_document retornó None para RESPONSE_ERROR — archivo físico guardado sin registro en BD")
else:
logger.info(f"RESPONSE_ERROR registrado en BD: id={doc_result.get('id')}, document_type={doc_result.get('document_type')}")
raise HTTPException( raise HTTPException(
status_code=500, status_code=500,
detail=create_error_response( detail=create_error_response(
message="Error en la respuesta del servicio SOAP", message="Error en la respuesta del servicio SOAP",
data={"soap_response": response.text[:500]} data={"soap_response": response.text[:3000]}
) )
) )

View File

@@ -4,6 +4,7 @@ import asyncio
import logging import logging
from typing import Dict, Any from typing import Dict, Any
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from fastapi import HTTPException
from .services import obtener_acuse from .services import obtener_acuse
from api.api_v2.modules.tasks.services import register_task, update_task from api.api_v2.modules.tasks.services import register_task, update_task
@@ -88,6 +89,6 @@ def process_acuse_request(self, acuse_request: Dict[str, Any]) -> Dict[str, Any]
) )
except Exception as update_error: except Exception as update_error:
logging.error(f"Error al actualizar estado de tarea: {update_error}") logging.error(f"Error al actualizar estado de tarea: {update_error}")
raise return {"status": "failed", "message": error_message}
finally: finally:
loop.close() loop.close()

View File

@@ -91,12 +91,13 @@ async def consume_ws_get_cove(**kwargs):
# Enviar documento de request a EFC # Enviar documento de request a EFC
try: try:
file_name_request = f"vu_COVE_{pedimento_app}_{cove}_REQUEST.xml" file_name_request = f"vu_COVE_{pedimento_app}_{cove}_REQUEST.xml"
document_response = await coves_rest_controller.post_document( document_response = await coves_rest_controller.post_or_update_document(
soap_response=soap_xml, soap_response=soap_xml,
organizacion=kwargs.get('pedimento').get('organizacion'), organizacion=kwargs.get('pedimento').get('organizacion'),
pedimento=kwargs.get('pedimento').get('id'), pedimento=kwargs.get('pedimento').get('id'),
file_name=file_name_request, file_name=file_name_request,
document_type=19, document_type=19,
identifier=cove,
) )
except Exception as e: except Exception as e:
logger.error(f"Error al enviar documento request: {e}") logger.error(f"Error al enviar documento request: {e}")
@@ -117,12 +118,13 @@ async def consume_ws_get_cove(**kwargs):
raise Exception("No se recibió respuesta del servicio SOAP") raise Exception("No se recibió respuesta del servicio SOAP")
if soap_error(soap_response): if soap_error(soap_response):
document_response = await coves_rest_controller.post_document( document_response = await coves_rest_controller.post_or_update_document(
soap_response=soap_response, soap_response=soap_response,
organizacion=kwargs.get('pedimento').get('organizacion'), organizacion=kwargs.get('pedimento').get('organizacion'),
pedimento=kwargs.get('pedimento').get('id'), pedimento=kwargs.get('pedimento').get('id'),
file_name=f"vu_COVE_{pedimento_app}_{cove}_ERROR.xml", file_name=f"vu_COVE_{pedimento_app}_{cove}_ERROR.xml",
document_type=20, document_type=20,
identifier=cove,
) )
raise Exception("Error en la respuesta del servicio SOAP") raise Exception("Error en la respuesta del servicio SOAP")
@@ -205,12 +207,13 @@ async def consume_ws_get_acuse_cove(**kwargs):
# Enviar documento de request a EFC # Enviar documento de request a EFC
try: try:
file_name_request = f"vu_AC_COVE_{kwargs.get('pedimento', {}).get('pedimento_app', 'N/A')}_{kwargs['cove'].get('cove', 'N/A')}_REQUEST.xml" file_name_request = f"vu_AC_COVE_{kwargs.get('pedimento', {}).get('pedimento_app', 'N/A')}_{kwargs['cove'].get('cove', 'N/A')}_REQUEST.xml"
document_response = await coves_rest_controller.post_document( document_response = await coves_rest_controller.post_or_update_document(
soap_response=soap_xml, soap_response=soap_xml,
organizacion=kwargs.get('pedimento').get('organizacion'), organizacion=kwargs.get('pedimento').get('organizacion'),
pedimento=kwargs.get('pedimento').get('id'), pedimento=kwargs.get('pedimento').get('id'),
file_name=file_name_request, file_name=file_name_request,
document_type=23, document_type=23,
identifier=kwargs['cove'].get('cove', 'N/A'),
) )
except Exception as e: except Exception as e:
logger.error(f"Error al enviar documento request de acuse cove: {e}") logger.error(f"Error al enviar documento request de acuse cove: {e}")
@@ -251,12 +254,13 @@ async def consume_ws_get_acuse_cove(**kwargs):
if soap_error(response): if soap_error(response):
error_file_name = f"vu_AC_COVE_{kwargs.get('pedimento', {}).get('pedimento_app', 'N/A')}_{kwargs['cove'].get('cove', 'N/A')}_ERROR.xml" error_file_name = f"vu_AC_COVE_{kwargs.get('pedimento', {}).get('pedimento_app', 'N/A')}_{kwargs['cove'].get('cove', 'N/A')}_ERROR.xml"
try: try:
rest_response = await coves_rest_controller.post_document( rest_response = await coves_rest_controller.post_or_update_document(
soap_response=response, soap_response=response,
organizacion=kwargs.get('pedimento').get('organizacion'), organizacion=kwargs.get('pedimento').get('organizacion'),
pedimento=kwargs.get('pedimento').get('id'), pedimento=kwargs.get('pedimento').get('id'),
file_name=error_file_name, file_name=error_file_name,
document_type=24, document_type=24,
identifier=kwargs['cove'].get('cove', 'N/A'),
) )
except Exception as e: except Exception as e:
logger.error(f"Error al guardar respuesta SOAP errónea: {e}") logger.error(f"Error al guardar respuesta SOAP errónea: {e}")

View File

@@ -22,7 +22,8 @@ def process_cove_request(self, cove_request: Dict[str, Any]) -> Dict[str, Any]:
organizacion_id = pedimento_info.get('organizacion') organizacion_id = pedimento_info.get('organizacion')
pedimento_app = pedimento_info.get('pedimento_app', 'N/A') pedimento_app = pedimento_info.get('pedimento_app', 'N/A')
cove_info = cove_request.get('cove', {}) cove_info = cove_request.get('cove', {})
cove_number = cove_info.get('numero_cove', 'N/A') # aqui se cambio de cove_number a 'cove' porque no esta asi en el mapeo
cove_number = cove_info.get('cove', 'N/A')
try: try:
# Registrar el inicio de la tarea # Registrar el inicio de la tarea
@@ -97,7 +98,8 @@ def process_acuse_cove_request(self, cove_request: Dict[str, Any]) -> Dict[str,
organizacion_id = pedimento_info.get('organizacion') organizacion_id = pedimento_info.get('organizacion')
pedimento_app = pedimento_info.get('pedimento_app', 'N/A') pedimento_app = pedimento_info.get('pedimento_app', 'N/A')
cove_info = cove_request.get('cove', {}) cove_info = cove_request.get('cove', {})
cove_number = cove_info.get('numero_cove', 'N/A') # se cambio el cove_number, ya que no existe directamente en el mapeo, se conoce solo como 'cove'
cove_number = cove_info.get('cove', 'N/A')
try: try:
# Registrar el inicio de la tarea # Registrar el inicio de la tarea

View File

@@ -48,12 +48,13 @@ async def obtener_edoc(**kwargs):
file_name_request = f"VU_ED_{pedimento_app}_{numero_documento}_REQUEST.xml" file_name_request = f"VU_ED_{pedimento_app}_{numero_documento}_REQUEST.xml"
document_response = await edocs_rest_controller.post_document( document_response = await edocs_rest_controller.post_or_update_document(
soap_response=soap_xml, soap_response=soap_xml,
organizacion=organizacion_efc, organizacion=organizacion_efc,
pedimento=pedimento_id_efc, pedimento=pedimento_id_efc,
file_name=file_name_request, file_name=file_name_request,
document_type=21 # Tipo de documento para request de e-document, document_type=21,
identifier=numero_documento,
) )
except Exception as e: except Exception as e:
@@ -97,6 +98,21 @@ async def obtener_edoc(**kwargs):
if soap_error(response): if soap_error(response):
logger.error("Respuesta SOAP contiene error de VUCEM") logger.error("Respuesta SOAP contiene error de VUCEM")
_pedimento_efc = kwargs.get('pedimento', {})
_file_name_error = f"VU_ED_{_pedimento_efc.get('pedimento_app', 'N/A')}_{numero_documento}_RESPONSE_ERROR.xml"
logger.info(f"Guardando RESPONSE_ERROR doc_type=22: file={_file_name_error}, organizacion={_pedimento_efc.get('organizacion')}, pedimento={_pedimento_efc.get('id')}")
_doc_result = await edocs_rest_controller.post_or_update_document(
soap_response=response.text,
organizacion=_pedimento_efc.get('organizacion'),
pedimento=_pedimento_efc.get('id'),
file_name=_file_name_error,
document_type=22,
identifier=numero_documento,
)
if _doc_result is None:
logger.error("post_or_update_document retornó None para RESPONSE_ERROR doc_type=22 — archivo físico sin registro en BD")
else:
logger.info(f"RESPONSE_ERROR registrado en BD: id={_doc_result.get('id')}, document_type={_doc_result.get('document_type')}")
raise HTTPException( raise HTTPException(
status_code=500, status_code=500,
detail=create_error_response( detail=create_error_response(
@@ -109,8 +125,23 @@ async def obtener_edoc(**kwargs):
try: try:
edoc_base64 = extract_pdf_bytes_from_xml_content(response.text) edoc_base64 = extract_pdf_bytes_from_xml_content(response.text)
except ValueError as ve: except Exception as ve:
logger.error(f"Error extrayendo contenido del XML: {ve}") logger.error(f"Error extrayendo contenido del XML: {ve}")
_pedimento_efc = kwargs.get('pedimento', {})
_file_name_error = f"VU_ED_{_pedimento_efc.get('pedimento_app', 'N/A')}_{numero_documento}_RESPONSE_ERROR.xml"
logger.info(f"Guardando RESPONSE_ERROR doc_type=22 (parse error): file={_file_name_error}")
_doc_result = await edocs_rest_controller.post_or_update_document(
soap_response=response.text,
organizacion=_pedimento_efc.get('organizacion'),
pedimento=_pedimento_efc.get('id'),
file_name=_file_name_error,
document_type=22,
identifier=numero_documento,
)
if _doc_result is None:
logger.error("post_document retornó None para RESPONSE_ERROR doc_type=22 (parse error)")
else:
logger.info(f"RESPONSE_ERROR registrado en BD: id={_doc_result.get('id')}, document_type={_doc_result.get('document_type')}")
raise HTTPException( raise HTTPException(
status_code=500, status_code=500,
detail=create_error_response( detail=create_error_response(
@@ -180,6 +211,8 @@ async def obtener_edoc(**kwargs):
document_type=5 document_type=5
) )
print(f"rest_response >>>> {rest_response}")
if rest_response is None: if rest_response is None:
logger.error("Error al enviar el documento a la API interna") logger.error("Error al enviar el documento a la API interna")
raise HTTPException( raise HTTPException(
@@ -239,9 +272,10 @@ async def obtener_edoc(**kwargs):
async def change_edocument_status(edoc: dict, status: bool, pedimento: dict): async def change_edocument_status(edoc: dict, status: bool, pedimento: dict):
# estaba acualizando mal el status de descarga, actualizaba otro campo que no le correspondia
data = { data = {
"id": edoc.get("id"), "id": edoc.get("id"),
"acuse_descargado": status, "edocument_descargado": status,
"numero_edocument": edoc.get("numero_edocument"), "numero_edocument": edoc.get("numero_edocument"),
"pedimento": pedimento.get("id"), "pedimento": pedimento.get("id"),
"organizacion": pedimento.get("organizacion"), "organizacion": pedimento.get("organizacion"),
@@ -252,42 +286,42 @@ async def change_edocument_status(edoc: dict, status: bool, pedimento: dict):
return response return response
NS = {
's': 'http://schemas.xmlsoap.org/soap/envelope/',
't': 'http://tempuri.org/',
'i': 'http://www.w3.org/2001/XMLSchema-instance'
}
# mejorar la extraccion de seccion File
def extract_pdf_bytes_from_xml_content(xml_content: str): def extract_pdf_bytes_from_xml_content(xml_content: str):
"""
Extrae el PDF y metadatos desde un string XML.
"""
root = ET.fromstring(xml_content) root = ET.fromstring(xml_content)
file_elem = root.find('.//File')
if file_elem is None: errores = root.find('.//t:Errores', NS)
for elem in root.iter(): tiene_error = root.find('.//t:TieneError', NS)
if elem.tag.endswith('File') and elem.text:
file_elem = elem if tiene_error is not None and tiene_error.text == 'true':
break err_msg = errores.text if errores is not None else 'Error desconocido'
if file_elem is not None and file_elem.text and file_elem.text.strip(): raise Exception(f"VUCEM informa error: {err_msg}")
base64_data = file_elem.text.strip().replace('\n', '').replace('\r', '')
pdf_bytes = base64.b64decode(base64_data) file_elem = root.find('.//t:File', NS)
cadena_original = None if file_elem is None or file_elem.get('{http://www.w3.org/2001/XMLSchema-instance}nil') == 'true' or not file_elem.text:
sello_digital = None raise ValueError("No se encontró el tag <File> con contenido válido.")
cadena_elem = root.find('.//CadenaOriginal')
if cadena_elem is None: base64_data = file_elem.text.strip().replace('\n', '').replace('\r', '')
for elem in root.iter(): pdf_bytes = base64.b64decode(base64_data)
if elem.tag.endswith('CadenaOriginal') and elem.text:
cadena_elem = elem # Extraer CadenaOriginal y SelloDigital con namespaces
break cadena_original = None
if cadena_elem is not None and cadena_elem.text: sello_digital = None
cadena_original = cadena_elem.text.strip() cadena_elem = root.find('.//t:CadenaOriginal', NS)
sello_elem = root.find('.//SelloDigital') if cadena_elem is not None and cadena_elem.get('{http://www.w3.org/2001/XMLSchema-instance}nil') != 'true':
if sello_elem is None: cadena_original = cadena_elem.text.strip() if cadena_elem.text else None
for elem in root.iter(): sello_elem = root.find('.//t:SelloDigital', NS)
if elem.tag.endswith('SelloDigital') and elem.text: if sello_elem is not None and sello_elem.get('{http://www.w3.org/2001/XMLSchema-instance}nil') != 'true':
sello_elem = elem sello_digital = sello_elem.text.strip() if sello_elem.text else None
break
if sello_elem is not None and sello_elem.text: return {
sello_digital = sello_elem.text.strip() "pdf_bytes": pdf_bytes,
return { "cadena_original": cadena_original,
"pdf_bytes": pdf_bytes, "sello_digital": sello_digital
"cadena_original": cadena_original, }
"sello_digital": sello_digital
}
else:
raise ValueError("No se encontró el tag <File> con contenido válido. Verifique que el XML contiene el tag <File> con datos base64.")

View File

@@ -21,7 +21,8 @@ def process_edoc_download_request(self, edoc_data: Dict) -> Dict:
organizacion_id = pedimento_info.get('organizacion') organizacion_id = pedimento_info.get('organizacion')
pedimento_app = pedimento_info.get('pedimento_app', 'N/A') pedimento_app = pedimento_info.get('pedimento_app', 'N/A')
edoc_info = edoc_data.get('edoc', {}) edoc_info = edoc_data.get('edoc', {})
edoc_number = edoc_info.get('numero_edoc', 'N/A') # el mapeo de numero de documento no estaba correcto, se corrigio
edoc_number = edoc_info.get('numero_edocument', 'N/A')
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)
@@ -36,7 +37,7 @@ def process_edoc_download_request(self, edoc_data: Dict) -> Dict:
message=f"Iniciando proceso de descarga de E-document {edoc_number} para pedimento {pedimento_app}", message=f"Iniciando proceso de descarga de E-document {edoc_number} para pedimento {pedimento_app}",
pedimento_id=pedimento_id, pedimento_id=pedimento_id,
organizacion_id=organizacion_id, organizacion_id=organizacion_id,
servicio=3 # 3 corresponde a "E-document" servicio=7 # cambiamos el servicio, el 7 le corresponde a los e documents
) )
) )
@@ -52,7 +53,7 @@ def process_edoc_download_request(self, edoc_data: Dict) -> Dict:
message=f"Descargando E-document {edoc_number} para pedimento {pedimento_app}", message=f"Descargando E-document {edoc_number} para pedimento {pedimento_app}",
pedimento_id=pedimento_id, pedimento_id=pedimento_id,
organizacion_id=organizacion_id, organizacion_id=organizacion_id,
servicio=3 servicio=7 # cambiamos el servicio, el 7 le corresponde a los e documents
) )
) )
@@ -67,7 +68,7 @@ def process_edoc_download_request(self, edoc_data: Dict) -> Dict:
message=f"E-document {edoc_number} descargado exitosamente para pedimento {pedimento_app}", message=f"E-document {edoc_number} descargado exitosamente para pedimento {pedimento_app}",
pedimento_id=pedimento_id, pedimento_id=pedimento_id,
organizacion_id=organizacion_id, organizacion_id=organizacion_id,
servicio=3 servicio=7 # cambiamos el servicio, el 7 le corresponde a los e documents
) )
) )
@@ -86,7 +87,7 @@ def process_edoc_download_request(self, edoc_data: Dict) -> Dict:
message=error_message, message=error_message,
pedimento_id=pedimento_id, pedimento_id=pedimento_id,
organizacion_id=organizacion_id, organizacion_id=organizacion_id,
servicio=3 servicio=7 # cambiamos el servicio, el 7 le corresponde a los e documents
) )
) )
except Exception as update_error: except Exception as update_error:

View File

@@ -240,7 +240,7 @@ class PedimentoXMLScraper: # Clase me extrae datos de Pedimento
# Extraer complemento1 (con namespace) # Extraer complemento1 (con namespace)
complemento1_elem = identificador.find('ns:complemento1', namespaces) complemento1_elem = identificador.find('ns:complemento1', namespaces)
complemento1 = complemento1_elem.text if complemento1_elem is not None else None complemento1 = complemento1_elem.text if complemento1_elem is not None else None
# Agregar a la lista si tenemos los datos básicos # Agregar a la lista si tenemos los datos básicos
if clave and complemento1: if clave and complemento1:
identificadores_ed.append({ identificadores_ed.append({

View File

@@ -64,12 +64,12 @@ async def obtener_remesa(**kwargs) -> Dict[str, Any]:
try: try:
file_name_request = f"vu_RM_{pedimento_data.get('pedimento_app', 'unknown')}_REQUEST.xml" file_name_request = f"vu_RM_{pedimento_data.get('pedimento_app', 'unknown')}_REQUEST.xml"
document_response = await remesa_rest_controller.post_document( document_response = await remesa_rest_controller.post_or_update_document(
soap_response=soap_xml, soap_response=soap_xml,
organizacion=pedimento_data.get('organizacion'), organizacion=pedimento_data.get('organizacion'),
pedimento=pedimento_data.get('id'), pedimento=pedimento_data.get('id'),
file_name=file_name_request, file_name=file_name_request,
document_type=15, document_type=15,
) )
except Exception as e: except Exception as e:
@@ -90,12 +90,12 @@ async def obtener_remesa(**kwargs) -> Dict[str, Any]:
if soap_error(soap_response): if soap_error(soap_response):
file_name = f"vu_RM_{pedimento_data.get('pedimento_app', 'unknown')}_ERROR.xml" file_name = f"vu_RM_{pedimento_data.get('pedimento_app', 'unknown')}_ERROR.xml"
document_response = await remesa_rest_controller.post_document( document_response = await remesa_rest_controller.post_or_update_document(
soap_response=soap_response, soap_response=soap_response,
organizacion=pedimento_data.get('organizacion'), organizacion=pedimento_data.get('organizacion'),
pedimento=pedimento_data.get('id'), pedimento=pedimento_data.get('id'),
file_name=file_name, file_name=file_name,
document_type=16, document_type=16,
) )
# Aquí necesitamos extraer el mensaje de error real # Aquí necesitamos extraer el mensaje de error real
@@ -134,12 +134,12 @@ async def obtener_remesa(**kwargs) -> Dict[str, Any]:
# Enviar documento # Enviar documento
try: try:
document_response = await remesa_rest_controller.post_document( document_response = await remesa_rest_controller.post_or_update_document(
soap_response=soap_response, soap_response=soap_response,
organizacion=pedimento_data.get('organizacion'), organizacion=pedimento_data.get('organizacion'),
pedimento=pedimento_data.get('id'), pedimento=pedimento_data.get('id'),
file_name=file_name, file_name=file_name,
document_type=5, document_type=3,
) )
except Exception as e: except Exception as e:
@@ -149,12 +149,14 @@ async def obtener_remesa(**kwargs) -> Dict[str, Any]:
# Extraer datos del XML # Extraer datos del XML
try: try:
remesas_data = remesa_xml_scraper.extract_remesas(soap_response.text) remesas_data = remesa_xml_scraper.extract_remesas(soap_response.text)
logger.info(
f"Remesas extraídas para pedimento {pedimento_data.get('pedimento_app')}: "
f"{len(remesas_data)} COVEs encontrados → {remesas_data}"
)
except Exception as e: except Exception as e:
logger.error(f"Error al extraer datos XML: {e}") logger.error(f"Error al extraer datos XML: {e}")
raise HTTPException(status_code=500, detail="Error al procesar respuesta XML") raise HTTPException(status_code=500, detail="Error al procesar respuesta XML")
logger.info(f"Remesa procesada exitosamente: {pedimento_data.get('pedimento')}") logger.info(f"Remesa procesada exitosamente: {pedimento_data.get('pedimento')}")
return create_service_response( return create_service_response(
@@ -202,14 +204,16 @@ async def post_remesa_data(**kwargs) -> Dict[str, Any]:
# Obtener datos del servicio web # Obtener datos del servicio web
try: try:
ws_data = await obtener_remesa(**kwargs) ws_data = await obtener_remesa(**kwargs)
result["documento"] = ws_data.get("documento", None) # obtener_remesa devuelve create_service_response → los datos están bajo 'data'
xml_content = ws_data.get('xml_content', {}) ws_data_content = ws_data.get('data', {})
result["documento"] = ws_data_content.get("documento", None)
xml_content = ws_data_content.get('xml_content', [])
result["xml_content"] = xml_content result["xml_content"] = xml_content
if not xml_content: if not xml_content:
logger.warning("No se obtuvo contenido XML del servicio web") logger.warning("No se obtuvo contenido XML del servicio web (lista de remesas vacía)")
return result return result
except HTTPException: except HTTPException:
raise # Re-lanzar HTTPExceptions raise # Re-lanzar HTTPExceptions
except Exception as e: except Exception as e:
@@ -259,55 +263,61 @@ async def _process_coves_safely(kwargs: Dict[str, Any], xml_content) -> Optional
async def _post_coves(pedimento_data: Dict[str, Any], coves: List[Dict[str, str]]) -> List[Dict[str, Any]]: async def _post_coves(pedimento_data: Dict[str, Any], coves: List[Dict[str, str]]) -> List[Dict[str, Any]]:
""" """
Envía COVEs al sistema REST. Crea COVEs en el sistema REST usando patrón get-or-create para evitar duplicados.
Si el COVE ya existe (creado por pedimento completo u otra remesa), lo reutiliza.
Args:
pedimento_data: Datos del pedimento
coves: Lista de diccionarios con datos de COVE (comprobanteVE, remesaAgente, remesaSA)
Returns:
Lista de respuestas exitosas
Raises:
HTTPException: Si no se pudo procesar ningún COVE
""" """
if not coves: if not coves:
return [] return []
responses = [] responses = []
errors = [] errors = []
for cove in coves: for cove in coves:
# Extraer el número de COVE del diccionario
numero_cove = cove.get('comprobanteVE') numero_cove = cove.get('comprobanteVE')
if not numero_cove: if not numero_cove:
logger.warning(f"COVE sin comprobanteVE encontrado: {cove}") logger.warning(f"COVE sin comprobanteVE encontrado: {cove}")
continue continue
document_data = {
'numero_cove': numero_cove,
'organizacion': pedimento_data.get('organizacion'),
'pedimento': pedimento_data.get('id')
}
try: try:
# Verificar si el COVE ya existe antes de intentar crearlo
existing = await remesa_rest_controller._make_request_async(
'GET', f'customs/coves/?numero_cove={numero_cove}'
)
if existing:
results = existing.get('results', existing) if isinstance(existing, dict) else existing
if isinstance(results, list) and results:
logger.info(f"COVE {numero_cove} ya existe (id={results[0].get('id')}), omitiendo creación")
responses.append(results[0])
continue
# No existe → crear
document_data = {
'numero_cove': numero_cove,
'organizacion': pedimento_data.get('organizacion'),
'pedimento': pedimento_data.get('id'),
}
response = await remesa_rest_controller.post_cove(document_data) response = await remesa_rest_controller.post_cove(document_data)
if response: if response:
responses.append(response) responses.append(response)
logger.debug(f"COVE {numero_cove} procesado exitosamente") logger.info(f"COVE {numero_cove} creado exitosamente")
else:
error_msg = f"POST de COVE {numero_cove} devolvió respuesta vacía"
logger.warning(error_msg)
errors.append(error_msg)
except Exception as e: except Exception as e:
error_msg = f"Error al procesar COVE {numero_cove}: {str(e)}" error_msg = f"Error al procesar COVE {numero_cove}: {str(e)}"
logger.warning(error_msg) logger.warning(error_msg)
errors.append(error_msg) errors.append(error_msg)
if not responses and coves: if not responses and coves:
error_detail = f"No se pudo procesar ningún COVE. Errores: {'; '.join(errors)}" error_detail = f"No se pudo procesar ningún COVE. Errores: {'; '.join(errors)}"
logger.error(error_detail) logger.error(error_detail)
raise HTTPException(status_code=500, detail=error_detail) raise HTTPException(status_code=500, detail=error_detail)
if errors: if errors:
logger.warning(f"Se procesaron {len(responses)}/{len(coves)} COVEs. Errores: {len(errors)}") logger.warning(f"Se procesaron {len(responses)}/{len(coves)} COVEs. Errores: {len(errors)}")
return responses return responses

View File

@@ -19,9 +19,11 @@ def process_remesa_request(self, remesa_request: dict) -> dict:
""" """
task_id = self.request.id task_id = self.request.id
servicio = 5 # Código para Pedimento Remesas servicio = 5 # Código para Pedimento Remesas
print(f"remesa_request >>>> {remesa_request}")
logger.info(f"remesa_request >>>> {remesa_request}")
pedimento_id = remesa_request.get('pedimento', {}).get('id') pedimento_id = remesa_request.get('pedimento', {}).get('id')
organizacion_id = remesa_request.get('pedimento', {}).get('organizacion') organizacion_id = remesa_request.get('pedimento', {}).get('organizacion')
remesa_num = remesa_request.get('remesa', 'N/A') remesa_num = remesa_request.get('pedimento', {}).get('pedimento_app', 'N/A')
# Crear un NUEVO event loop para esta tarea (evita problemas de loop cerrado) # Crear un NUEVO event loop para esta tarea (evita problemas de loop cerrado)
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()

View File

@@ -1,6 +1,8 @@
from celery import Celery from celery import Celery
from core.config import settings from core.config import settings
import os import os
from celery.schedules import crontab
from datetime import timedelta
# Configuración de Celery # Configuración de Celery
celery_app = Celery( celery_app = Celery(
@@ -32,6 +34,15 @@ celery_app.conf.update(
task_save_success_on_complete=True # Guarda los resultados exitosos task_save_success_on_complete=True # Guarda los resultados exitosos
) )
# desde aqui se pueden programar tareas pero al estar desde microservicios no tengo acceso a la informacion de organizacion
# por este motivo es mejor programar las tareas desde la aplicacion de django
celery_app.conf.beat_schedule = {
# 'prueba-4': {
# 'task': 'tasks.prueba_hola',
# 'schedule': timedelta(seconds=60), # 12:00 = 6:03 AM
# },
}
# Autodiscovery of tasks # Autodiscovery of tasks
celery_app.autodiscover_tasks() celery_app.autodiscover_tasks()

View File

@@ -91,6 +91,46 @@ class APIRESTController:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return None return None
async def post_or_update_document(
self, soap_response=None, organizacion: str = None,
pedimento: str = None, file_name: str = None,
document_type: int = None, fuente: int = 2,
identifier: str = None,
) -> Dict[str, Any]:
"""
Guarda un documento VU (request o error). Si ya existe uno del mismo
tipo/pedimento/identificador, elimina el anterior y crea el nuevo,
evitando duplicados en ejecuciones recurrentes (tarea programada diaria).
identifier: cadena única del documento dentro del pedimento
(ej: número de COVE, número de e-document). Se usa como
filtro archivo__icontains para distinguir documentos del
mismo tipo pertenecientes a distintos COVEs o edocs.
"""
try:
query = f'record/documents/?pedimento={pedimento}&document_type={document_type}'
if identifier:
query += f'&archivo__icontains={identifier}'
existing = await self._make_request_async('GET', query)
if existing:
results = existing.get('results', existing) if isinstance(existing, dict) else existing
if isinstance(results, list) and results:
existing_id = results[0].get('id')
if existing_id:
await self._make_request_async('DELETE', f'record/documents/{existing_id}/')
logger.info(f"Documento VU previo eliminado: id={existing_id}, document_type={document_type}, identifier={identifier}")
except Exception as e:
logger.warning(f"No se pudo verificar/eliminar documento VU existente (document_type={document_type}, identifier={identifier}): {e}")
return await self.post_document(
soap_response=soap_response,
organizacion=organizacion,
pedimento=pedimento,
file_name=file_name,
document_type=document_type,
fuente=fuente,
)
async def put_procesamiento(self, service_id: int, data: Dict[str, Any]) -> Dict[str, Any]: async def put_procesamiento(self, service_id: int, data: Dict[str, Any]) -> Dict[str, Any]:
return await self._make_request_async('PUT', f'customs/procesamientopedimentos/{service_id}/', data=data) return await self._make_request_async('PUT', f'customs/procesamientopedimentos/{service_id}/', data=data)
@@ -457,13 +497,16 @@ class APIController:
async def put_edocument(self, edocument_id: str, data: Dict[str, Any]) -> Dict[str, Any]: async def put_edocument(self, edocument_id: str, data: Dict[str, Any]) -> Dict[str, Any]:
""" """
Método para actualizar un documento digitalizado en la API. Método para actualizar un documento digitalizado en la API.
Args: Args:
edocument_id: UUID del documento a actualizar edocument_id: UUID del documento a actualizar
data: Diccionario con los datos a actualizar data: Diccionario con los datos a actualizar
""" """
return await self._make_request_async('PUT', f'customs/edocuments/{edocument_id}/', data=data) return await self._make_request_async('PUT', f'customs/edocuments/{edocument_id}/', data=data)
async def put_cove(self, cove_id: str, data: Dict[str, Any]) -> Dict[str, Any]:
return await self._make_request_async('PUT', f'customs/coves/{cove_id}/', data=data)
async def get_key(self, id: str) -> bytes: async def get_key(self, id: str) -> bytes:
result = await self._make_request_async('GET', f'vucem/vucem/{id}/download_key/', return_bytes=True) result = await self._make_request_async('GET', f'vucem/vucem/{id}/download_key/', return_bytes=True)

View File

@@ -125,9 +125,12 @@ def pedimento_completo_task(self, request_data: Dict[str, Any]):
# Subir documento de pedimento completo si la petición fue exitosa # Subir documento de pedimento completo si la petición fue exitosa
try: try:
# solucion al error de descarga de un e-document, el mapeo de identificador no llegaba y ni siquiera insertaba registros en
# la tabla
identificadores_ed_response = xml_content.get('identificadores_ed', []) if 'xml_content' in locals() else []
upload_result = await _post_edocuments( upload_result = await _post_edocuments(
response_service=service_data, response_service=service_data,
identificadores_ed=[soap_response.get('documento', {}).get('numero_edocument')] identificadores_ed=identificadores_ed_response
) )
logger.info(f"Documento de pedimento completo subido exitosamente: {upload_result}") logger.info(f"Documento de pedimento completo subido exitosamente: {upload_result}")
except Exception as upload_err: except Exception as upload_err:
@@ -626,6 +629,19 @@ def acuse_task(self, **kwargs):
documento_info["documento"] = soap_response.get('documento', {}) documento_info["documento"] = soap_response.get('documento', {})
documentos_exitosos += 1 documentos_exitosos += 1
logger.info(f"Acuse del documento {idx + 1} procesado exitosamente") logger.info(f"Acuse del documento {idx + 1} procesado exitosamente")
try:
await rest_controller.put_edocument(
edocument_id=edoc['id'],
data={
"id": edoc['id'],
"acuse_descargado": True,
"numero_edocument": edoc.get('numero_edocument'),
"pedimento": service_data['pedimento']['id'],
"organizacion": service_data['organizacion'],
}
)
except Exception as status_err:
logger.warning(f"Error actualizando acuse_descargado para edoc {edoc.get('numero_edocument')}: {status_err}")
else: else:
documento_info["error"] = "Error en petición SOAP" documento_info["error"] = "Error en petición SOAP"
logger.warning(f"No se pudo procesar el acuse del documento {idx + 1}") logger.warning(f"No se pudo procesar el acuse del documento {idx + 1}")
@@ -832,28 +848,19 @@ def edocument_task(self, **kwargs):
documento_info["documento"] = soap_response.get('documento', {}) documento_info["documento"] = soap_response.get('documento', {})
documentos_exitosos += 1 documentos_exitosos += 1
logger.info(f"E-document {idx + 1} procesado exitosamente") logger.info(f"E-document {idx + 1} procesado exitosamente")
# Subir el documento si la petición fue exitosa
try: try:
upload_result = await _post_edocuments( await rest_controller.put_edocument(
response_service=service_data, edocument_id=edoc['id'],
identificadores_ed=[edoc.get('numero_edocument')] data={
"id": edoc['id'],
"edocument_descargado": True,
"numero_edocument": edoc.get('numero_edocument'),
"pedimento": service_data['pedimento']['id'],
"organizacion": service_data['organizacion'],
}
) )
documento_info["upload_result"] = upload_result except Exception as status_err:
logger.info(f"Documento {edoc.get('numero_edocument')} subido exitosamente") logger.warning(f"Error actualizando edocument_descargado para edoc {edoc.get('numero_edocument')}: {status_err}")
except Exception as upload_err:
documento_info["upload_error"] = str(upload_err)
logger.error(f"Error al subir documento {edoc.get('numero_edocument')}: {upload_err}")
# Subir el documento si la petición fue exitosa
try:
upload_result = await _post_edocuments(
response_service=service_data,
identificadores_ed=[edoc.get('numero_edocument')]
)
documento_info["upload_result"] = upload_result
logger.info(f"Documento {edoc.get('numero_edocument')} subido exitosamente")
except Exception as upload_err:
documento_info["upload_error"] = str(upload_err)
logger.error(f"Error al subir documento {edoc.get('numero_edocument')}: {upload_err}")
else: else:
documento_info["error"] = "Error en petición SOAP" documento_info["error"] = "Error en petición SOAP"
logger.warning(f"No se pudo procesar el e-document {idx + 1}") logger.warning(f"No se pudo procesar el e-document {idx + 1}")
@@ -974,6 +981,19 @@ def coves_task(self, **kwargs):
documento_info["documento"] = soap_response.get('documento', {}) documento_info["documento"] = soap_response.get('documento', {})
documentos_exitosos += 1 documentos_exitosos += 1
logger.info(f"cove del documento {idx + 1} procesado exitosamente") logger.info(f"cove del documento {idx + 1} procesado exitosamente")
try:
await rest_controller.put_cove(
cove_id=cove['id'],
data={
"id": cove['id'],
"cove_descargado": True,
"numero_cove": cove.get('numero_cove'),
"pedimento": service_data['pedimento']['id'],
"organizacion": service_data['organizacion'],
}
)
except Exception as status_err:
logger.warning(f"Error actualizando cove_descargado para cove {cove.get('numero_cove')}: {status_err}")
else: else:
documento_info["error"] = "Error en petición SOAP" documento_info["error"] = "Error en petición SOAP"
logger.warning(f"No se pudo procesar el cove del documento {idx + 1}") logger.warning(f"No se pudo procesar el cove del documento {idx + 1}")
@@ -1115,17 +1135,19 @@ def acuse_cove_task(self, **kwargs):
documento_info["documento"] = soap_response.get('documento', {}) documento_info["documento"] = soap_response.get('documento', {})
documentos_exitosos += 1 documentos_exitosos += 1
logger.info(f"Acuse de COVE {idx + 1} procesado exitosamente") logger.info(f"Acuse de COVE {idx + 1} procesado exitosamente")
# Subir el documento de COVE si la petición fue exitosa
try: try:
upload_result = await _post_coves( await rest_controller.put_cove(
response_service=service_data, cove_id=cove['id'],
identificadores_cove=[cove.get('numero_cove')] data={
"id": cove['id'],
"acuse_cove_descargado": True,
"numero_cove": cove.get('numero_cove'),
"pedimento": service_data['pedimento']['id'],
"organizacion": service_data['organizacion'],
}
) )
documento_info["upload_result"] = upload_result except Exception as status_err:
logger.info(f"Documento COVE {cove.get('numero_cove')} subido exitosamente") logger.warning(f"Error actualizando acuse_cove_descargado para cove {cove.get('numero_cove')}: {status_err}")
except Exception as upload_err:
documento_info["upload_error"] = str(upload_err)
logger.error(f"Error al subir documento COVE {cove.get('numero_cove')}: {upload_err}")
else: else:
documento_info["error"] = "Error en petición SOAP" documento_info["error"] = "Error en petición SOAP"
logger.warning(f"No se pudo procesar el acuse de COVE {idx + 1}") logger.warning(f"No se pudo procesar el acuse de COVE {idx + 1}")
@@ -1166,3 +1188,12 @@ def acuse_cove_task(self, **kwargs):
raise e raise e
return run_async_task(_execute_acuse_cove) return run_async_task(_execute_acuse_cove)
# @celery_app.task(name='tasks.prueba_hola')
# def prueba_hola():
# """
# Tarea de prueba: solo imprime un saludo para verificar la programación.
# """
# logger.info("¡Hola! Tarea programada ejecutada correctamente.")
# print("¡Hola desde la tarea programada!")
# return "OK"

View File

@@ -603,7 +603,7 @@ async def get_soap_acuse(credenciales, response_service, soap_controller, edocum
no_partidas = response_service['pedimento'].get('numero_partidas', 0) no_partidas = response_service['pedimento'].get('numero_partidas', 0)
tipo_operacion = response_service['pedimento'].get('tipo_operacion', 'N/A') tipo_operacion = response_service['pedimento'].get('tipo_operacion', 'N/A')
pedimento = response_service['pedimento'].get('pedimento', 'N/A') pedimento = response_service['pedimento'].get('pedimento', 'N/A')
_file_name = f"vu_AC_{remesas}{no_partidas}{tipo_operacion}_{aduana}_{patente}_{pedimento}_{idx}.pdf" _file_name = f"vu_AC_{remesas}{no_partidas}{tipo_operacion}_{aduana}_{patente}_{pedimento}_{edocument['numero_edocument']}.pdf"
# Enviar el documento PDF usando binary_content # Enviar el documento PDF usando binary_content
logger.info(f"Enviando documento PDF: {_file_name} ({len(pdf_bytes)} bytes)") logger.info(f"Enviando documento PDF: {_file_name} ({len(pdf_bytes)} bytes)")
@@ -710,7 +710,7 @@ async def get_soap_acuseCOVE(credenciales, response_service, soap_controller, co
no_partidas = response_service['pedimento'].get('numero_partidas', 0) no_partidas = response_service['pedimento'].get('numero_partidas', 0)
tipo_operacion = response_service['pedimento'].get('tipo_operacion', 'N/A') tipo_operacion = response_service['pedimento'].get('tipo_operacion', 'N/A')
pedimento = response_service['pedimento'].get('pedimento', 'N/A') pedimento = response_service['pedimento'].get('pedimento', 'N/A')
_file_name = f"vu_AC_COVE_{remesas}{no_partidas}{tipo_operacion}_{aduana}_{patente}_{pedimento}_{idx}.pdf" _file_name = f"vu_AC_COVE_{remesas}{no_partidas}{tipo_operacion}_{aduana}_{patente}_{pedimento}_{cove['numero_cove']}.pdf"
# Enviar el documento PDF usando binary_content # Enviar el documento PDF usando binary_content
logger.info(f"Enviando documento PDF: {_file_name} ({len(pdf_bytes)} bytes)") logger.info(f"Enviando documento PDF: {_file_name} ({len(pdf_bytes)} bytes)")
@@ -884,7 +884,7 @@ async def get_soap_edocument(credenciales, response_service, soap_controller, ed
no_partidas = response_service['pedimento'].get('numero_partidas', 0) no_partidas = response_service['pedimento'].get('numero_partidas', 0)
tipo_operacion = response_service['pedimento'].get('tipo_operacion', 'N/A') tipo_operacion = response_service['pedimento'].get('tipo_operacion', 'N/A')
pedimento = response_service['pedimento'].get('pedimento', 'N/A') pedimento = response_service['pedimento'].get('pedimento', 'N/A')
_file_name = f"vu_EDC_{remesas}{no_partidas}{tipo_operacion}_{aduana}_{patente}_{pedimento}_{idx}.pdf" _file_name = f"vu_EDC_{remesas}{no_partidas}{tipo_operacion}_{aduana}_{patente}_{pedimento}_{edocument['numero_edocument']}.pdf"
# Enviar el documento PDF usando binary_content # Enviar el documento PDF usando binary_content
logger.info(f"Enviando documento PDF: {_file_name} ({len(pdf_bytes)} bytes)") logger.info(f"Enviando documento PDF: {_file_name} ({len(pdf_bytes)} bytes)")