diff --git a/api/api_v2/api.py b/api/api_v2/api.py index 0a49a1e..30e3c5f 100644 --- a/api/api_v2/api.py +++ b/api/api_v2/api.py @@ -2,17 +2,17 @@ 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 +# from api.api_v2.modules.acuses import router as acuses_router +from api.api_v2.modules.coves.router 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(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"]) +# 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/coves/router.py b/api/api_v2/modules/coves/router.py index e69de29..b43cb76 100644 --- a/api/api_v2/modules/coves/router.py +++ b/api/api_v2/modules/coves/router.py @@ -0,0 +1,12 @@ +from fastapi import APIRouter, HTTPException +from .schema import CoveBaseSchema +from typing import List +from uuid import UUID + +router = APIRouter() +# Aquí puedes definir tus endpoints relacionados con COVES usando el esquema CoveBaseSchema + +@router.post("/cove/", response_model=CoveBaseSchema) +async def create_cove(cove: CoveBaseSchema): + # Lógica para crear un COVE + return cove \ No newline at end of file diff --git a/api/api_v2/modules/coves/schema.py b/api/api_v2/modules/coves/schema.py index e69de29..8637266 100644 --- a/api/api_v2/modules/coves/schema.py +++ b/api/api_v2/modules/coves/schema.py @@ -0,0 +1,12 @@ +from pydantic import BaseModel, Field, field_validator +from typing import Optional +from uuid import UUID + + +from api.api_v2.modules.pedimentos.schema import PedimentoBaseSchema +from schemas.CredencialSchema import CredencialBaseSchema + +class CoveBaseSchema(BaseModel): + cove: str = Field(..., description="ID del COVE asociado") + pedimento: PedimentoBaseSchema + credenciales: CredencialBaseSchema diff --git a/api/api_v2/modules/coves/service.py b/api/api_v2/modules/coves/service.py index e69de29..772ed00 100644 --- a/api/api_v2/modules/coves/service.py +++ b/api/api_v2/modules/coves/service.py @@ -0,0 +1,105 @@ +import base64 +from http.client import HTTPException +import os +from controllers.RESTController import rest_controller +from controllers.SOAPController import soap_controller +from cryptography.hazmat.primitives import serialization, hashes +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives.serialization import load_der_private_key +import tempfile + + +def sign_chain_original(key_path: str, password: str, cadena_original: str) -> str: + with open(key_path, 'rb') as key_file: + private_key = load_der_private_key( + key_file.read(), + password=password.encode() if password else None + ) + + signature = private_key.sign( + cadena_original.encode(), + padding.PKCS1v15(), + hashes.SHA256() + ) + + return base64.b64encode(signature).decode() + +async def fetch_sign_and_cer(cadena_original: str, username: str, credenciales: dict, **kwargs): + cer = await rest_controller.get_cer(credenciales['id']) + if cer is None: + raise HTTPException(status_code=500, detail="No se pudo obtener el certificado para firmar el COVE") + certificado = base64.b64encode(cer).decode('utf-8') + + # Obtener la key como binario y guardarla en un archivo temporal + import tempfile + key_bytes = await rest_controller.get_key(credenciales['id']) + if key_bytes is None: + raise HTTPException(status_code=500, detail="No se pudo obtener la llave privada para firmar el COVE") + with tempfile.NamedTemporaryFile(delete=False) as tmp_key_file: + tmp_key_file.write(key_bytes) + tmp_key_path = tmp_key_file.name + + # Usar la ruta temporal para firmar + firma = sign_chain_original(tmp_key_path, credenciales['efirma'], cadena_original) + return firma, certificado, tmp_key_path + +async def consume_ws_get_cove(**kwargs): + + # valdiar kwargs + + # Cadena original que vas a firmar + + try: + cadena_original = f"|{username}|{cove['numero_cove']}|" + firma, certificado, tmp_key_path = await fetch_sign_and_cer(cadena_original, username, credenciales, **kwargs) + os.remove(tmp_key_path) # Eliminar el archivo temporal después de usarlo + + soap_xml = soap_controller.generate_cove_template( + username=username, + password=credenciales['password'], + certificado=certificado, + firma=firma, + cove=cove, + ) + + soap_headers = { + 'Content-Type': 'text/xml; charset=utf-8', + 'SOAPAction': '', + #'Accept-Encoding': 'gzip,deflate', + } + + soap_response = await soap_controller.make_request_async( + "ventanilla/ConsultarEdocumentService?wsdl", + data=soap_xml, + headers=soap_headers + ) + + if (soap_response) and (not soap_error(soap_response)): + remesas = 1 if response_service['pedimento'].get('remesas', 0) else 0 + patente = response_service['pedimento'].get('patente', 'N/A') + aduana = response_service['pedimento'].get('aduana', 'N/A') + no_partidas = response_service['pedimento'].get('numero_partidas', 0) + 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}_{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, + ) + + return { + "servicio": response_service, + "documento": document_response + } + else: + raise HTTPException(status_code=500, detail="Error en la petición SOAP al servicio VUCEM") + except HTTPException: + # Re-lanzar HTTPExceptions sin modificar + raise + except Exception as e: + import traceback + raise HTTPException(status_code=500, detail=f"Error interno al procesar acuse cove: {str(e)}") diff --git a/api/api_v2/modules/pedimentos/schema.py b/api/api_v2/modules/pedimentos/schema.py index e69de29..24e4efc 100644 --- a/api/api_v2/modules/pedimentos/schema.py +++ b/api/api_v2/modules/pedimentos/schema.py @@ -0,0 +1,24 @@ + +from typing import Optional +from pydantic import BaseModel + + +class PedimentoBaseSchema(BaseModel): + id: str + pedimento: str + pedimento_app: str + aduana: str + patente: str + regimen: str + organizacion: str + clave_pedimento: str + fecha_pago: Optional[str] + fecha_inicio: Optional[str] + fecha_fin: Optional[str] + alerta: Optional[bool] + agente_aduanal: Optional[str] + curp_apoderado: Optional[str] + importe_total: Optional[float] + saldo_disponible: Optional[float] + importe_pedimento: Optional[float] + existe_expediente: Optional[bool] diff --git a/main.py b/main.py index ebccc99..d7f042d 100644 --- a/main.py +++ b/main.py @@ -2,6 +2,7 @@ import logging from fastapi import FastAPI from core.config import settings from api.api_v1.api import api_router +from api.api_v2.api import api_router as api_v2_router from fastapi.middleware.cors import CORSMiddleware # Configuración inicial del logging (debe estar al inicio del archivo) @@ -55,6 +56,7 @@ def create_application() -> FastAPI: # Incluir el router principal de la API application.include_router(api_router, prefix="/api/v1") + application.include_router(api_v2_router, prefix="/api/v2") return application diff --git a/schemas/CredencialSchema.py b/schemas/CredencialSchema.py new file mode 100644 index 0000000..7b79a28 --- /dev/null +++ b/schemas/CredencialSchema.py @@ -0,0 +1,15 @@ +from pydantic import BaseModel, Field +from typing import Optional +from uuid import UUID + +from schemas.importadorSchema import ImportadorBaseSchema + +class CredencialBaseSchema(BaseModel): + importadores: ImportadorBaseSchema + user: str = Field(..., description="Usuario de la credencial") + password: str = Field(..., description="Contraseña de la credencial") + efirma: str = Field(..., description="E-firma de la credencial") + key: str = Field(..., description="Key de la credencial") + cer: str = Field(..., description="Cer de la credencial") + is_active: bool = Field(..., description="Indica si la credencial está activa") + organizacion: UUID = Field(..., description="ID de la organización asociada") \ No newline at end of file diff --git a/schemas/importadorSchema.py b/schemas/importadorSchema.py new file mode 100644 index 0000000..152527b --- /dev/null +++ b/schemas/importadorSchema.py @@ -0,0 +1,8 @@ +from pydantic import BaseModel, Field +from typing import Optional +from uuid import UUID + +class ImportadorBaseSchema(BaseModel): + rfc: str = Field(..., description="RFC del importador") + nombre: Optional[str] = Field(None, description="Nombre del importador") + organizacion: UUID = Field(..., description="ID de la organización asociada")