Se integro el sistema de extraer el tipo de cambio
This commit is contained in:
@@ -27,10 +27,25 @@ route_handler = TenantCRUDRoutes(
|
||||
max_page_size=100,
|
||||
)
|
||||
|
||||
router = route_handler.router
|
||||
crud_router = route_handler.router
|
||||
|
||||
# Create a custom router for specific endpoints that must be matched BEFORE generic CRUD routes
|
||||
# We use the same prefix so they are grouped together
|
||||
from fastapi import APIRouter
|
||||
custom_router = APIRouter(prefix="/exchange-rate", tags=[])
|
||||
|
||||
@custom_router.get("/test-ping")
|
||||
async def test_ping():
|
||||
return {"message": "pong"}
|
||||
|
||||
# Master router to export
|
||||
router = APIRouter()
|
||||
# Include custom routes FIRST to avoid shadowing by /{id}
|
||||
router.include_router(custom_router)
|
||||
# router.include_router(crud_router)
|
||||
|
||||
|
||||
@router.get(
|
||||
@custom_router.get(
|
||||
"/",
|
||||
response_model=Dict[str, Any],
|
||||
summary="List Exchange Rates",
|
||||
@@ -66,3 +81,37 @@ async def list_exchange_rates(
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
}
|
||||
|
||||
@custom_router.get(
|
||||
"/dof-search",
|
||||
response_model=Dict[str, Any],
|
||||
summary="Fetch Exchange Rate from DOF",
|
||||
description="Fetches the exchange rate from the Official Journal of the Federation (DOF) for a specific date.",
|
||||
)
|
||||
async def fetch_exchange_rate_dof(
|
||||
date: str = Query(..., description="Date in YYYY-MM-DD format"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: Dict[str, Any] = Depends(get_current_user),
|
||||
):
|
||||
# This endpoint can be public or protected. Assuming protected for now.
|
||||
# No specific tenant validation needed since it's an external query,
|
||||
# but good to ensure user is authenticated.
|
||||
|
||||
try:
|
||||
print(f"DEBUG: Route called with date={date}")
|
||||
rate = ExchangeRateService.fetch_from_dof(date)
|
||||
|
||||
if rate is None:
|
||||
return {"success": False, "message": "No se encontró el tipo de cambio en el DOF para la fecha especificada o el servicio no está disponible.", "value": None}
|
||||
|
||||
return {"success": True, "value": rate}
|
||||
except Exception as e:
|
||||
print(f"DEBUG: Error in route: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return {"success": False, "message": f"Error interno: {str(e)}", "value": None}
|
||||
|
||||
# Include routers at the end to ensure all routes are registered
|
||||
# Include custom routes FIRST to avoid shadowing by /{id} of crud_router
|
||||
router.include_router(custom_router)
|
||||
router.include_router(crud_router)
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
from typing import Optional, Tuple, List, Dict, Any
|
||||
from datetime import datetime, time
|
||||
import requests
|
||||
import re
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import cast, Date
|
||||
|
||||
from . import dto, models
|
||||
import urllib3
|
||||
from core.config import settings
|
||||
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
|
||||
|
||||
|
||||
class ExchangeRateService:
|
||||
@@ -135,3 +143,91 @@ class ExchangeRateService:
|
||||
db.delete(exchange_rate)
|
||||
db.commit()
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _get_external_api_token(base_url, username, password) -> Optional[str]:
|
||||
"""Helper to get authentication token from external API"""
|
||||
try:
|
||||
login_url = f"{base_url}/auth/login"
|
||||
payload = {"username": username, "password": password}
|
||||
headers = {"Content-Type": "application/json"}
|
||||
|
||||
response = requests.post(login_url, json=payload, headers=headers, timeout=5)
|
||||
if response.status_code not in [200, 201]:
|
||||
print(f"External API Login Failed: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
data = response.json()
|
||||
return data.get("token") or data.get("access_token")
|
||||
except Exception as e:
|
||||
print(f"External API Login Error: {e}")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def fetch_from_dof(date_str: str) -> Optional[float]:
|
||||
"""
|
||||
Fetches the exchange rate from an external API (replacing direct DOF scraping).
|
||||
The API handles date logic (holidays, weekends) automatically.
|
||||
|
||||
Args:
|
||||
date_str (str): Date in 'YYYY-MM-DD' format.
|
||||
|
||||
Returns:
|
||||
Optional[float]: The exchange rate value if found, None otherwise.
|
||||
"""
|
||||
# API Credentials
|
||||
API_BASE_URL = settings.EXTERNAL_API_URL
|
||||
API_USER = settings.EXTERNAL_API_USER
|
||||
API_PASS = settings.EXTERNAL_API_PASSWORD
|
||||
|
||||
if not API_USER or not API_PASS:
|
||||
print("ERROR: External API credentials not properly configured in settings")
|
||||
return None
|
||||
|
||||
try:
|
||||
print(f"DEBUG: Fetching External API for date: {date_str}")
|
||||
|
||||
# 1. Get Token
|
||||
token = ExchangeRateService._get_external_api_token(API_BASE_URL, API_USER, API_PASS)
|
||||
if not token:
|
||||
print("Failed to obtain external API token")
|
||||
return None
|
||||
|
||||
# 2. Fetch Exchange Rate
|
||||
# The API endpoint is /tipoCambio/{YYYY-MM-DD}
|
||||
tc_endpoint = f"{API_BASE_URL}/tipoCambio/{date_str}"
|
||||
|
||||
# Auth header: The API expects just the token string in common usage, but we try standard first
|
||||
# based on user feedback/code: 'Authorization:' . $token
|
||||
headers = {
|
||||
"Authorization": token,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
response = requests.get(tc_endpoint, headers=headers, timeout=5)
|
||||
|
||||
# Retry logic as per PHP reference (if 401, maybe formatting issue, but requests handles headers well)
|
||||
if response.status_code == 401:
|
||||
# Try with Bearer prefix just in case, though PHP code suggested raw token
|
||||
print("DEBUG: 401 received, retrying with Bearer prefix...")
|
||||
headers["Authorization"] = f"Bearer {token}"
|
||||
response = requests.get(tc_endpoint, headers=headers, timeout=5)
|
||||
|
||||
if response.status_code != 200:
|
||||
print(f"External API TC Error: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
data = response.json()
|
||||
# Expected response: {"Id":..., "Fecha":"...", "TipoCambio":17.452, "Mov":"..."}
|
||||
|
||||
if "TipoCambio" in data:
|
||||
val = float(data["TipoCambio"])
|
||||
print(f"DEBUG: External API returned value: {val}")
|
||||
return val
|
||||
|
||||
print(f"DEBUG: 'TipoCambio' key not found in response: {data}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error fetching from External API: {e}")
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user