# Anexo76 - Resumen de Arquitectura Técnica

## 📋 Índice
1. [Visión General](#visión-general)
2. [Stack Tecnológico](#stack-tecnológico)
3. [Arquitectura del Sistema](#arquitectura-del-sistema)
4. [Arquitectura de Schemas y Módulos](#arquitectura-de-schemas-y-módulos)
5. [Estructura del Proyecto](#estructura-del-proyecto)
6. [Flujos Principales](#flujos-principales)
7. [Seguridad](#seguridad)
8. [Base de Datos](#base-de-datos)
9. [API Reference](#api-reference)

---

## Visión General

Anexo76 es una aplicación SaaS multi-tenant para gestión de comercio exterior en México, enfocada en cumplir con los Anexos 24, 31 y 22 del SAT.

### Objetivos de Negocio
- Gestión de inventarios para maquilas e IMMEX
- Control de pedimentos aduanales
- Manejo de facturas de importación/exportación
- Cumplimiento normativo SAT
- Licenciamiento flexible por planes

---

## Stack Tecnológico

### Backend
- **Framework**: FastAPI 0.110+ (Python 3.11+)
- **ORM**: SQLAlchemy 2.0
- **Autenticación**: Keycloak (OpenID Connect)
- **Base de Datos**: PostgreSQL 15+
- **Validación**: Pydantic 2.6+
- **Testing**: Pytest

### Frontend
- **Framework**: SvelteKit 2.0+ (Svelte 5)
- **Lenguaje**: TypeScript
- **Auth Client**: keycloak-js
- **Estilos**: TailwindCSS 4.1+
- **Build**: Vite 7+

### Infraestructura
- **Containerización**: Docker / Docker Compose
- **Orquestación**: Kubernetes (futuro)
- **CI/CD**: GitHub Actions / GitLab CI
- **Monitoreo**: Prometheus + Grafana

---

## Arquitectura del Sistema

### Patrón Arquitectónico: Modular Layered (estilo NestJS)

```
┌─────────────────────────────────────────────────────────┐
│                     FRONTEND                            │
│  SvelteKit + Keycloak-js + TailwindCSS                 │
└────────────────┬────────────────────────────────────────┘
                 │ HTTP/REST + JWT
┌────────────────▼────────────────────────────────────────┐
│                  API GATEWAY (FastAPI)                  │
│  Middleware: Tenant | License | Logging | CORS         │
└────────────────┬────────────────────────────────────────┘
                 │
        ┌────────┴────────┐
        │                 │
┌───────▼──────┐  ┌──────▼────────┐
│   MODULES    │  │  CORE LAYER   │
│              │  │               │
│ • auth       │  │ • config.py   │
│ • tenants    │  │ • database.py │
│ • licenses   │  │ • security.py │
│ • ...        │  │ • middleware  │
└───────┬──────┘  └───────────────┘
        │
┌───────▼──────────────────────────┐
│  DATABASE LAYER (Multi-tenant)   │
│                                  │
│  ┌──────────┐  ┌──────────────┐  │
│  │ Core DB  │  │ Tenant 1 DB  │  │
│  │ (shared) │  │ (dedicated)  │  │
│  └──────────┘  └──────────────┘  │
└──────────────────────────────────┘
```

### Estructura Modular (por módulo)

Cada módulo sigue el patrón:

```
modules/{module_name}/
├── models.py      # ORM Models (SQLAlchemy)
├── dto.py         # Data Transfer Objects (Pydantic)
├── service.py     # Business Logic Layer
├── routes.py      # API Endpoints (FastAPI)
└── __init__.py    # Module exports
```

#### Responsabilidades por Capa

1. **models.py**: Representación de entidades en BD
   - Define tablas con SQLAlchemy
   - Relaciones entre entidades
   - Constraints y validaciones a nivel DB

2. **dto.py**: Contratos de entrada/salida de datos
   - DTOs de request (CreateDTO, UpdateDTO)
   - DTOs de response (ResponseDTO)
   - Validaciones de Pydantic

3. **service.py**: Lógica de negocio
   - Operaciones CRUD
   - Validaciones de negocio
   - Orquestación de operaciones complejas

4. **routes.py**: Exposición HTTP
   - Definición de endpoints
   - Documentación OpenAPI automática
   - Manejo de dependencias (auth, db)

---

## Arquitectura de Schemas y Módulos

### Estructura de Schemas en Base de Datos

La aplicación utiliza una arquitectura de schemas para organizar lógicamente las tablas según su funcionalidad y alcance:

#### **Schema `a24` (Anexo 24)**
Contiene todas las tablas relacionadas con el **Anexo 24 del SAT** (control de inventarios para empresas IMMEX):
- Gestión de inventarios
- Control de entradas y salidas de mercancías
- Reportes de existencias
- Cumplimiento de obligaciones fiscales del Anexo 24

#### **Schema `a76` (Anexo 76)**
Contiene todas las tablas relacionadas con el **Anexo 76 del SAT** (comercio exterior):
- Pedimentos aduanales
- Facturas de importación/exportación
- Documentación de comercio exterior
- Cumplimiento normativo de comercio exterior

#### **Schema `public` (Catálogos Fijos)**
Contiene **catálogos compartidos** y datos de referencia que no cambian frecuentemente:
- Catálogos del SAT (tipos de material, unidades de medida, etc.)
- Códigos de país
- Catálogos de aduanas
- Tipos de documento
- Datos maestros compartidos entre módulos

### Convención de Prefijos de Tablas

Para mantener claridad y trazabilidad, las tablas utilizan prefijos que identifican su módulo funcional:

#### **Prefijo `inv_` (Inventarios)**
Tablas relacionadas con el **control de inventarios**:
- `inv_products`: Productos en inventario
- `inv_movements`: Movimientos de entrada/salida
- `inv_warehouses`: Almacenes
- `inv_balances`: Saldos de inventario

**Nota histórica**: Anteriormente se utilizaba el prefijo `s` (SCAII - Sistema de aduanas e Inventarios).

#### **Prefijo `fa_` (Fixed Assets / Activos Fijos)**
Tablas relacionadas con la **gestión de activos fijos**:
- `fa_assets`: Registro de activos fijos
- `fa_depreciation`: Depreciación de activos
- `fa_maintenance`: Mantenimiento de activos
- `fa_transfers`: Transferencias de activos

**Nota histórica**: Anteriormente se utilizaba el prefijo `q` (SCAF - Sistema de Control de Activos Fijos).

### Diagrama de Arquitectura de Schemas

```
┌─────────────────────────────────────────────────────────────┐
│                    DATABASE: anexo76_db                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌────────────────┐  ┌────────────────┐  ┌───────────────┐  │
│  │  Schema: a24   │  │  Schema: a76   │  │Schema: public │  │
│  │  (Anexo 24)    │  │  (Anexo 76)    │  │ (Catálogos)   │  │
│  ├────────────────┤  ├────────────────┤  ├───────────────┤  │
│  │                │  │                │  │               │  │
│  │ inv_products   │  │ pedimentos     │  │ material_types│  │
│  │ inv_movements  │  │ facturas       │  │ uom_codes     │  │
│  │ inv_warehouses │  │ customs_docs   │  │ countries     │  │
│  │ inv_balances   │  │ export_ops     │  │ customs_list  │  │
│  │                │  │                │  │ document_types│  │
│  │ fa_assets      │  │                │  │               │  │
│  │ fa_depreciation│  │                │  │               │  │
│  │ fa_maintenance │  │                │  │               │  │
│  │ fa_transfers   │  │                │  │               │  │
│  │                │  │                │  │               │  │
│  └────────────────┘  └────────────────┘  └───────────────┘  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

### Ventajas de esta Arquitectura

1. **Separación Lógica**: Cada schema representa un dominio específico del negocio
2. **Escalabilidad**: Facilita la adición de nuevos módulos sin afectar los existentes
3. **Seguridad**: Permite aplicar permisos a nivel de schema
4. **Mantenibilidad**: Código y migraciones organizados por dominio
5. **Claridad**: Los prefijos hacen evidente la funcionalidad de cada tabla
6. **Migración Gradual**: Permite actualizar sistemas legados (SCAII/SCAF) sin interrupciones

### Mapeo de Sistemas Legados

| Sistema Legacy       | Prefijo Antiguo | Sistema Nuevo     | Prefijo Nuevo | Schema   |
|----------------------|-----------------|-------------------|---------------|----------|
| SCAII (Inventarios)  | `s`             | Inventarios       | `inv_`        | `a24`    |
| SCAF (Activos Fijos) | `q`             | Fixed Assets      | `fa_`         | `a24`    |
| Winsaii (Pedimentos) | `w`             | -                 | -             | `a22`    |
| -                    | `g`             | Comercio Exterior | -             | `a76`    |
| -                    | `g`             | Catálogos SAT     | -             | `public` |

---

## Estructura del Proyecto

```
anexo76/
├── backend/
│   ├── main.py                    # Aplicación FastAPI principal
│   ├── requirements.txt           # Dependencias
│   ├── init_db.py                # Script de inicialización
│   ├── Dockerfile
│   │
│   ├── core/                     # Capa core (shared)
│   │   ├── config.py             # Configuración (Pydantic Settings)
│   │   ├── database.py           # Gestión de BD multi-tenant
│   │   ├── security.py           # Auth Keycloak + JWT
│   │   ├── middleware.py         # Middlewares personalizados
│   │   └── __init__.py
│   │
│   └── api/
│       └── v1/
│           ├── router.py         # Router principal v1
│           ├── common/           # Utilidades compartidas
│           │   ├── base_models.py
│           │   ├── crud_routes.py
│           │   ├── dto_mixins.py
│           │   └── tenant_crud_routes.py
│           │
│           └── modules/          # Módulos de negocio por schema
│               ├── a24/          # Módulo Anexo 24 (Inventarios)
│               │   ├── inventarios/
│               │   └── activos_fijos/
│               │
│               ├── a76/          # Módulo Anexo 76 (Comercio Exterior)
│               │   ├── pedimentos/
│               │   └── facturas/
│               │
│               └── public/       # Catálogos compartidos
│                   ├── material_types/
│                   ├── uom_codes/
│                   └── countries/
│
├── frontend/
│   ├── src/
│   │   ├── routes/               # Páginas SvelteKit
│   │   │   ├── +layout.svelte   # Layout global con Keycloak
│   │   │   ├── +page.svelte     # Dashboard principal
│   │   │   └── callback/        # OAuth callback
│   │   │
│   │   └── lib/
│   │       ├── auth.ts           # Servicio de autenticación
│   │       └── api.ts            # Cliente API
│   │
│   ├── static/
│   │   └── silent-check-sso.html
│   ├── package.json
│   └── Dockerfile
│
├── docs/
│   ├── KEYCLOAK_SETUP.md        # Guía de configuración
│   └── ARCHITECTURE.md          # Este documento
│
├── docker-compose.yml            # Orquestación completa
├── start.sh                      # Script de inicio rápido
├── README.md                     # Documentación principal
└── .gitignore
```

---

## Flujos Principales

### 1. Flujo de Autenticación

```
┌──────────┐                  ┌──────────┐                ┌──────────┐
│ Frontend │                  │ Keycloak │                │ Backend  │
└────┬─────┘                  └────┬─────┘                └────┬─────┘
     │                             │                           │
     │ 1. Clic "Login"             │                           │
     ├────────────────────────────>│                           │
     │                             │                           │
     │ 2. Formulario de login      │                           │
     │<────────────────────────────┤                           │
     │                             │                           │
     │ 3. Credenciales             │                           │
     ├────────────────────────────>│                           │
     │                             │                           │
     │ 4. Redirigir + auth code    │                           │
     │<────────────────────────────┤                           │
     │                             │                           │
     │ 5. Intercambiar code x token│                           │
     ├────────────────────────────>│                           │
     │                             │                           │
     │ 6. JWT (access + refresh)   │                           │
     │<────────────────────────────┤                           │
     │                             │                           │
     │ 7. Request con Bearer token │                           │
     ├─────────────────────────────┼──────────────────────────>│
     │                             │                           │
     │                             │ 8. Validar token          │
     │                             │<──────────────────────────┤
     │                             │                           │
     │                             │ 9. Public key             │
     │                             ├──────────────────────────>│
     │                             │                           │
     │ 10. Respuesta con datos     │                           │
     │<─────────────────────────────┼───────────────────────────┤
     │                             │                           │
```

### 2. Flujo de Request Multi-tenant

```
Request con JWT
     ↓
TenantMiddleware
├─ Extrae tenant_id del token
├─ Valida tenant existe y está activo
└─ Agrega tenant_id a request.state
     ↓
LicenseValidationMiddleware
├─ Consulta licencia del tenant
├─ Valida estado (active/expired)
├─ Valida fecha de vigencia
└─ Agrega license_info a request.state
     ↓
Endpoint Handler
├─ Obtiene tenant_id de request.state
├─ Selecciona BD (shared o dedicated)
└─ Procesa request
     ↓
Response
```

### 3. Flujo de Selección de Base de Datos

```python
# Pseudocódigo
tenant_id = request.state.tenant_id

tenant = db.query(Tenant).filter(Tenant.id == tenant_id).first()

if tenant.type == "SHARED":
    # Usar BD compartida (core_db)
    db_session = CoreSessionLocal()
    # Queries incluyen tenant_id en WHERE
    
elif tenant.type == "DEDICATED":
    # Usar BD dedicada del tenant
    db_config = json.loads(tenant.db_config)
    db_session = get_tenant_db(tenant_id, db_config)
    # No necesita filtrar por tenant_id
```

---

## Seguridad

### Autenticación
- **Keycloak** como Identity Provider
- **OpenID Connect** (OIDC)
- **JWT** con RS256 (firma asimétrica)
- **Refresh tokens** para renovación

### Autorización
- **RBAC** (Role-Based Access Control)
- Roles: `admin`, `user`, `auditor`, `system`
- Middleware `has_role()` para proteger endpoints

### Multi-tenancy
- **Aislamiento por tenant_id** en JWT
- **Row-level security** en BD compartida
- **BD dedicada** para mayor aislamiento (enterprise)

### Row-Level Security (RLS) en BD compartida

> Convención alineada al skill `aduanasoft-dev-standards` (sección 10).
> La capa API sigue siendo responsable del control fino (roles/permisos
> con Keycloak + `PermissionService`); RLS añade **defensa en profundidad**
> a nivel de BD para que un bug en un `WHERE` no permita salirse del tenant.

#### Variables de sesión (`SET LOCAL`)

| GUC | Origen | Comportamiento RLS |
|-----|--------|--------------------|
| `app.tenant_id` | JWT (`TenantMiddleware`) → `request.state.tenant_id` | Obligatoria. Si está vacía, `app.current_tenant_id()` retorna `NULL` y las políticas devuelven `0` filas (fail-closed). |
| `app.company_id` | Header `X-Company-Id` o cookie `active_company_id` | Opcional. Si está vacía, el tenant ve **todas sus compañías** (útil para selectores de compañía y bootstrap). |

Ambas se fijan con `SET LOCAL` al inicio de cada transacción —
**nunca** con `SET` global, para no contaminar conexiones del pool.

Helpers SQL definidos por la migración `d1a2b3c4e5f6_enable_rls_tenant_company`:

```sql
CREATE FUNCTION app.current_tenant_id()  RETURNS INTEGER LANGUAGE sql STABLE AS
$$ SELECT NULLIF(current_setting('app.tenant_id',  true), '')::INTEGER $$;
CREATE FUNCTION app.current_company_id() RETURNS INTEGER LANGUAGE sql STABLE AS
$$ SELECT NULLIF(current_setting('app.company_id', true), '')::INTEGER $$;
```

#### Tipos de política

1. **Solo `tenant_id`** (p.ej. `a76.company`, `core.licenses`):
   `tenant_id = app.current_tenant_id()`.
2. **`tenant_id` + `company_id`** (`TenantScopedMixin`, mayoría de tablas
   `a24/`a76/`core`): además exige `company_id = app.current_company_id()`
   cuando esa GUC está fijada.
3. **Solo `company_id`** (algunas tablas `a76.company_*`): valida el
   `tenant_id` indirectamente vía `EXISTS` contra `a76.company`.

Todas las tablas usan `FORCE ROW LEVEL SECURITY` para que la política
aplique también al owner. Las únicas tablas core **excluidas** son
`core.tenants` y `core.user_tenants` — necesarias para el bootstrap del
selector de tenant antes de tener contexto fijado.

#### Propagación del contexto

| Camino | Cómo se fija el contexto |
|--------|--------------------------|
| HTTP request | `TenantMiddleware` rellena `request.state.tenant_id`/`company_id`; `get_core_db` / `get_async_core_db` leen esos valores y los guardan en `Session.info`. Un listener `after_begin` ejecuta `SET LOCAL` por transacción. |
| `LicenseValidationMiddleware` | Usa `scoped_core_db(tenant_id=...)` para que la consulta de licencia entre con contexto RLS válido. |
| Tareas Celery | `track_and_dispatch` inyecta `rls_tenant_id` / `rls_company_id` en los headers del task; los signals `task_prerun`/`task_postrun` los copian a `ContextVar`s del worker, que el listener `after_begin` consume como fallback. Tareas críticas (imports/exports de invoices, expediente) abren la sesión con `scoped_core_db(tenant_id=..., company_id=...)`. |
| Tests | Las suites de pytest pueden usar `scoped_core_db(...)` o emular el flujo con `set_config('app.tenant_id', ...)` antes del query. Hay un set de tests en `backend/tests/integration/test_rls_tenant_company.py` que valida aislamiento A vs B usando un rol sin `BYPASSRLS`. |

#### Reparto de responsabilidades

| Capa | Decide |
|------|--------|
| **API (FastAPI + Keycloak + `PermissionService`)** | Roles, permisos por compañía, accesos a recursos concretos (`validate_access_to_resource`), reglas de negocio. |
| **RLS (PostgreSQL)** | Límite estructural duro: `tenant_id` y `company_id`. **No** modela roles/permisos para evitar duplicar lógica fina con la API. |

#### Operación / DevOps

- En **producción** la API debe conectar con un rol **sin** `BYPASSRLS`
  (`postgres` superusuario lo bypassea por diseño). El `docker-compose.yml`
  de desarrollo usa `postgres` deliberadamente para no romper migraciones;
  los tests crean un rol `anexo76_rls_test` para ejercitar las políticas.
- Los jobs/ETL/migraciones que necesiten ver todos los tenants deben usar
  un rol técnico explícito con `BYPASSRLS` o fijar `app.tenant_id` por
  iteración — nunca asumir que la sesión global "ve todo".
- La migración `d1a2b3c4e5f6_enable_rls_tenant_company` tiene `downgrade()`
  completo (drop policies + `DISABLE ROW LEVEL SECURITY`) para revertir.

### Validación de Licencias
- Middleware verifica en cada request:
  - ✓ Licencia activa
  - ✓ No expirada
  - ✓ Límites no excedidos

---

## Base de Datos

### Modelo Híbrido Multi-tenant

#### BD Core (Compartida)
Tablas principales:
- `tenants`: Información de clientes
- `licenses`: Control de licencias por tenant
- `license_usage`: Métricas de uso
- `users` (futuro): Usuarios por tenant

Todas las tablas operacionales incluyen `tenant_id` para segmentación.

#### BD Dedicadas (Enterprise)
- Una BD PostgreSQL por tenant
- Configuración almacenada en `tenants.db_config`
- Migración automática desde BD compartida

### Ejemplo de Tabla Multi-tenant

```sql
CREATE TABLE inventories (
    id SERIAL PRIMARY KEY,
    tenant_id INTEGER NOT NULL REFERENCES tenants(id),
    product_code VARCHAR(50) NOT NULL,
    quantity INTEGER NOT NULL,
    created_at TIMESTAMP DEFAULT NOW(),
    
    -- Índice compuesto para queries eficientes
    INDEX idx_tenant_product (tenant_id, product_code)
);
```

### Migración y Upgrade

```python
# Tenant en BD compartida → BD dedicada
tenant_service.upgrade_to_dedicated(
    tenant_id=123,
    db_config={
        "host": "dedicated-postgres.example.com",
        "port": 5432,
        "name": "tenant_123_db",
        "user": "tenant_123_user",
        "password": "secure_password"
    }
)
```

---

## API Reference

### Módulo: Authentication (`/v1/auth`)

| Endpoint | Método | Descripción | Auth |
|----------|--------|-------------|------|
| `/auth/login` | POST | Login con Keycloak | Público |
| `/auth/refresh` | POST | Renovar access token | Público |
| `/auth/me` | GET | Info del usuario actual | Bearer |
| `/auth/logout` | POST | Cerrar sesión | Bearer |
| `/auth/health` | GET | Health check | Público |

### Módulo: Tenants (`/v1/tenants`)

| Endpoint | Método | Descripción | Rol Requerido |
|----------|--------|-------------|---------------|
| `/tenants` | POST | Crear tenant | admin |
| `/tenants` | GET | Listar tenants | admin |
| `/tenants/{id}` | GET | Obtener tenant | user |
| `/tenants/{id}` | PUT | Actualizar tenant | admin |
| `/tenants/{id}` | DELETE | Eliminar tenant | admin |
| `/tenants/slug/{slug}` | GET | Obtener por slug | user |

### Módulo: Licenses (`/v1/licenses`)

| Endpoint | Método | Descripción | Rol Requerido |
|----------|--------|-------------|---------------|
| `/licenses` | POST | Crear licencia | admin |
| `/licenses/tenant/{id}` | GET | Obtener licencia | user |
| `/licenses/tenant/{id}` | PUT | Actualizar licencia | admin |
| `/licenses/validate/{id}` | GET | Validar licencia | user |
| `/licenses/usage/{id}` | GET | Uso de licencia | user |
| `/licenses/my-license` | GET | Mi licencia | user |

### Planes de Licencia

| Plan | Usuarios | Storage | Operaciones/mes | Features |
|------|----------|---------|-----------------|----------|
| Free | 5 | 10 GB | 1,000 | API básica |
| Basic | 20 | 50 GB | 10,000 | + Reportes |
| Professional | 100 | 200 GB | 50,000 | + Integraciones |
| Enterprise | ∞ | ∞ | ∞ | + Soporte + BD dedicada |

---

## Próximas Implementaciones

### Backend
- [ ] Módulo de inventarios
- [ ] Módulo de pedimentos
- [ ] Módulo de facturas
- [ ] Webhooks para integraciones
- [ ] Reportes avanzados
- [ ] Export/Import de datos

### Frontend
- [ ] Dashboard con gráficas
- [ ] Gestión de inventarios UI
- [ ] Formularios de pedimentos
- [ ] Panel de administración
- [ ] Reportes interactivos

### DevOps
- [ ] CI/CD pipeline
- [ ] Tests automatizados
- [ ] Monitoreo con Prometheus
- [ ] Dashboards de Grafana
- [ ] Deploy a Kubernetes
- [ ] Backup automatizado

---

**Última actualización**: Octubre 2025  
**Versión del documento**: 1.0
