# SITAR API Module

Módulo reorganizado para integración con la API externa de SITAR (Sistema de Información de Aranceles).

## Estructura

Cada recurso ahora tiene su propia carpeta con separación de responsabilidades:

```
backend/api/v1/modules/sitar/
├── common/                 # Servicio base compartido
│   ├── __init__.py
│   └── base_service.py     # SitarAPIBaseService con autenticación
├── tlcs/                   # TLCS (Tratados de Libre Comercio)
│   ├── __init__.py
│   ├── schemas.py          # TLCSResponse
│   ├── service.py          # TLCSService
│   └── router.py           # Endpoints FastAPI
├── fracciones/             # Fracciones arancelarias mexicanas
├── fracciones_usa/         # Fracciones USA
├── regulaciones/           # Regulaciones y restricciones
├── prosec/                 # PROSEC
├── precios_estimados/      # Precios estimados
├── fundamentos_tlc/        # Fundamentos TLC
├── aladi2/                 # ALADI2
├── cuotas2/                # Cuotas compensatorias
├── cupos/                  # Cupos de importación
├── fracciones_anteriores/  # Historial de fracciones
├── informacion_general/    # Información general
├── ieps/                   # IEPS
├── noms/                   # Normas Oficiales Mexicanas
├── rcg2/                   # Reglas de Carácter General
├── reit/                   # REIT
├── requisito_previo/       # Requisitos previos
├── vehiculos_marcas/       # Marcas de vehículos
├── vehiculos_modelos/      # Modelos de vehículos
├── __init__.py             # Exporta todos los servicios
└── main_router.py          # Router principal con todos los endpoints
```

## Uso del Servicio

### Desde cualquier parte del código

Cada recurso tiene su propio servicio **singleton** que maneja automáticamente la autenticación:

```python
from api.v1.modules.sitar.tlcs import TLCSService
from api.v1.modules.sitar.fracciones import FraccionesService

# Obtener instancia singleton
tlcs_service = TLCSService.get_instance()

# Buscar TLCS (async)
tlcs_data = await tlcs_service.search(
    fraccion="84716001",
    pais="USA",
    limit=100
)

# Procesar resultados
for tlcs in tlcs_data:
    print(f"Tasa: {tlcs.TASATXT}, País: {tlcs.PAIS}")
```

### Desde código síncrono

Si estás en contexto síncrono, usa `asyncio.run()`:

```python
import asyncio
from api.v1.modules.sitar.tlcs import TLCSService

tlcs_service = TLCSService.get_instance()
tlcs_data = asyncio.run(tlcs_service.search(fraccion="84716001", pais="USA"))
```

## Métodos Disponibles

### TLCS (Tratados de Libre Comercio)

- `search_tlcs(fraccion, pais, nico, skip, limit)` - Buscar TLCS
- `get_tlcs_by_id(sysid, fraccion)` - Obtener TLCS por ID

### Fracciones Arancelarias

- `search_fracciones(fraccion, nico, skip, limit)` - Buscar fracciones mexicanas
- `get_fraccion_by_id(sysid)` - Obtener fracción por ID
- `search_fracciones_usa(fraccion, skip, limit)` - Buscar fracciones USA
- `get_fraccion_usa_by_id(consecutivo)` - Obtener fracción USA por ID
- `search_fracciones_anteriores(fraccion_actual, fraccion_anterior, skip, limit)` - Buscar historial de fracciones
- `get_fracciones_anteriores_by_id(sysid)` - Obtener historial por ID

### Regulaciones y Restricciones

- `search_regulaciones(fraccion, nico, skip, limit)` - Buscar regulaciones
- `get_regulacion_by_id(sysid)` - Obtener regulación por ID
- `search_noms(fraccion, pais, nico, skip, limit)` - Buscar Normas Oficiales Mexicanas
- `get_noms_by_id(sysid)` - Obtener NOM por ID
- `search_requisito_previo(fraccion, nico, skip, limit)` - Buscar requisitos previos
- `get_requisito_previo_by_id(sysid)` - Obtener requisito previo por ID

### PROSEC y Programas de Promoción

- `search_prosec(fraccion, nico, skip, limit)` - Buscar PROSEC
- `get_prosec_by_id(sysid)` - Obtener PROSEC por ID
- `search_reit(fraccion, nico, skip, limit)` - Buscar REIT
- `get_reit_by_id(sysid)` - Obtener REIT por ID

### Precios e Impuestos

- `search_precios_estimados(fraccion, nico, skip, limit)` - Buscar precios estimados
- `get_precio_estimado_by_id(sysid)` - Obtener precio estimado por ID
- `search_ieps(fraccion, nico, skip, limit)` - Buscar IEPS
- `get_ieps_by_id(consecutivo)` - Obtener IEPS por ID

### Fundamentos y Acuerdos

- `search_fundamentos_tlc(fraccion, nico, tipat_only, skip, limit)` - Buscar fundamentos TLC
- `get_fundamento_tlc_by_id(sysid)` - Obtener fundamento por ID
- `search_aladi2(fraccion, pais, nico, skip, limit)` - Buscar ALADI2
- `get_aladi2_by_id(sysid)` - Obtener ALADI2 por ID

### Cuotas y Cupos

- `search_cuotas2(fraccion, pais, nico, skip, limit)` - Buscar cuotas compensatorias
- `get_cuotas2_by_id(sysid)` - Obtener cuota por ID
- `search_cupos(fraccion, nico, skip, limit)` - Buscar cupos de importación
- `get_cupos_by_id(sysid)` - Obtener cupo por ID

### Reglas de Carácter General

- `search_rcg2(fraccion, nico, skip, limit)` - Buscar RCG2
- `get_rcg2_by_id(sysid)` - Obtener RCG2 por ID

### Información General

- `search_informacion_general(fraccion, nico, skip, limit)` - Buscar información general
- `get_informacion_general_by_id(sysid)` - Obtener información general por ID

### Vehículos

- `search_vehiculos_marcas(fraccion, marca, skip, limit)` - Buscar marcas de vehículos
- `get_vehiculos_marcas_by_id(sysid)` - Obtener marca por ID
- `search_vehiculos_modelos(fraccion, marca, modelo, skip, limit)` - Buscar modelos de vehículos
- `get_vehiculos_modelos_by_id(sysid)` - Obtener modelo por ID

## Configuración

Requiere las siguientes variables de entorno:

```bash
SITAR_API_URL=https://api.sitar.example.com
SITAR_API_USER=your_username
SITAR_API_PASSWORD=your_password
```

## Servicios Disponibles

Cada módulo sigue el mismo patrón con métodos `search()` y `get_by_id()`:

### TLCS - `TLCSService`

```python
from api.v1.modules.sitar.tlcs import TLCSService
service = TLCSService.get_instance()
await service.search(fraccion="84716001", pais="USA", nico=None, skip=0, limit=100)
await service.get_by_id(sysid=123, fraccion="84716001")
```

### Fracciones - `FraccionesService`

```python
from api.v1.modules.sitar.fracciones import FraccionesService
service = FraccionesService.get_instance()
await service.search(fraccion="84716001", nico=None, skip=0, limit=100)
await service.get_by_id(sysid=123)
```

### Fracciones USA - `FraccionesUSAService`

```python
from api.v1.modules.sitar.fracciones_usa import FraccionesUSAService
service = FraccionesUSAService.get_instance()
await service.search(fraccion="84716001", skip=0, limit=100)
await service.get_by_id(consecutivo=123)
```

### Otros servicios disponibles:

- **RegulacionesService** - Regulaciones y restricciones
- **ProsecService** - PROSEC (Programa de Promoción Sectorial)
- **PreciosEstimadosService** - Precios estimados
- **FundamentosTLCService** - Fundamentos de tratados de libre comercio
- **Aladi2Service** - ALADI2 (Asociación Latinoamericana de Integración)
- **Cuotas2Service** - Cuotas compensatorias
- **CuposService** - Cupos de importación
- **FraccionesAnterioresService** - Historial de fracciones
- **InformacionGeneralService** - Información general de fracciones
- **IepsService** - IEPS (Impuesto Especial sobre Producción y Servicios)
- **NomsService** - Normas Oficiales Mexicanas
- **Rcg2Service** - Reglas de Carácter General
- **ReitService** - Registro de Empresas de Industria Terminal
- **RequisitoPrevioService** - Requisitos previos
- **VehiculosMarcasService** - Marcas de vehículos
- **VehiculosModelosService** - Modelos de vehículos

```### Desde endpoints asíncronos

```python
from fastapi import APIRouter, HTTPException
from api.v1.modules.sitar.tlcs import TLCSService

router = APIRouter()

@router.get("/tariff/{fraccion}")
async def get_tariff_info(fraccion: str, country: str = "USA"):
    """Endpoint que consulta información arancelaria"""
    try:
        tlcs_service = TLCSService.get_instance()
        data = await tlcs_service.search(fraccion=fraccion, pais=country)
        return {"success": True, "data": data}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
```
## Arquitectura

### Servicio Base Compartido

Todos los servicios heredan de `SitarAPIBaseService` que maneja:

- Autenticación con token caching
- Renovación automática de tokens
- Manejo de errores HTTP
- Timeout configurable

### Singleton Pattern

Cada servicio usa el patrón singleton para:

- Compartir la conexión HTTP
- Reutilizar tokens de autenticación
- Evitar múltiples instancias

### Separación de Responsabilidades

- **schemas.py**: Modelos Pydantic para validación de datos
- **service.py**: Lógica de negocio y llamadas a API
- **router.py**: Endpoints FastAPI (opcional)
- **__init__.py**: Exportaciones públicas

## Notas Importantes

1. **Autenticación**: El token se cachea y renueva automáticamente
2. **Timeouts**: Configurado a 10 segundos por defecto
3. **Límites**: Máximo 1000 registros por consulta
4. **Errores**: Todos los servicios lanzan excepciones httpx en caso de error
5. **Async/Sync**: Los servicios son async, usa `asyncio.run()` en código síncrono

```

Esto expondrá endpoints como:
- `GET /api/v1/sitar/tlcs/` - Buscar TLCS
- `GET /api/v1/sitar/fracciones/` - Buscar fracciones
- etc.

## Ejemplo Completo

```python
from api.v1.modules.sitar import SitarAPIService

async def get_tariff_info(fraccion: str, country: str):
    """Obtener información arancelaria completa"""
    sitar = SitarAPIService.get_instance()
  
    try:
        # Buscar TLCS
        tlcs = await sitar.search_tlcs(fraccion=fraccion, pais=country)
      
        if tlcs:
            first_tlcs = tlcs[0]
            return {
                "rate": first_tlcs.TASATXT,
                "adv_impo": float(first_tlcs.TASA1NUM or 0.0),
                "country": first_tlcs.PAIS
            }
      
        # Si no hay TLCS, buscar fracción general
        fracciones = await sitar.search_fracciones(fraccion=fraccion)
      
        if fracciones:
            first_frac = fracciones[0]
            return {
                "rate": first_frac.ADVIMPOTXT,
                "adv_impo": float(first_frac.ADVIMPONUM or 0.0),
                "description": first_frac.DESCRIPCION
            }
          
        return None
      
    except Exception as e:
        print(f"Error: {e}")
        return None
```
## Manejo de Errores

El servicio lanza excepciones `httpx.HTTPError` en caso de error:

```python
import httpx

try:
    tlcs = await sitar.search_tlcs(fraccion="invalid")
except httpx.HTTPStatusError as e:
    print(f"HTTP Error: {e.response.status_code}")
except httpx.RequestError as e:
    print(f"Request Error: {e}")
except Exception as e:
    print(f"General Error: {e}")
```
