# Manual de Arquitectura Técnica y Modelo de Datos — Radar GovTech

Este documento describe la arquitectura interna, el modelo de datos, los patrones de concurrencia y los algoritmos del núcleo de **Radar** ([licitacion.co](https://licitacion.co)).

---

## 1. Principios de Diseño y Arquitectura General

Radar está concebido bajo una arquitectura **100% Client-Side** contenida en una aplicación de archivo único (`index.html`). Esta decisión elimina la necesidad de servidores de aplicación intermedios, maximizando la velocidad, reduciendo los costos de infraestructura a cero y garantizando una portabilidad absoluta en redes Edge CDN.

```mermaid
flowchart LR
    subgraph Cliente["Navegador Web (Client-Side)"]
        UI["DOM & Interfaz Reactiva"]
        Store["Estado en Memoria & localStorage"]
        Motor["Motor de Búsqueda & Caché"]
    end
    
    subgraph Socrata["Red de Datos Abiertos de Colombia"]
        Secop2["API Secop II (p6dx-8zbt)"]
        Secop1["API Secop I (f789-7hwg)"]
    end
    
    UI <--> Store
    UI <--> Motor
    Motor -- "Promise.allSettled" --> Secop2
    Motor -- "Promise.allSettled" --> Secop1
```

### Fortalezas de la Arquitectura
* **Zero-Backend:** No requiere Node.js, Python, PHP ni bases de datos SQL/NoSQL en servidor para operar.
* **Cero compilación:** Código JavaScript nativo (ES6+) moderno y Tailwind CSS con renderizado instantáneo.
* **Resiliencia de Red:** Si una de las bases de datos de Secop entra en mantenimiento, el sistema continúa funcionando con la otra fuente.
* **Velocidad Extrema:** Consultas directas desde el navegador del cliente hacia los servidores CDN de Socrata con compresión GZIP/Brotli.

---

## 2. Modelo y Normalización de Datos (Socrata Open Data)

Las dos plataformas estatales colombianas utilizan estructuras de datos heterogéneas. Radar implementa un adaptador que normaliza ambos esquemas en un modelo unificado en tiempo de ejecución:

| Atributo Unificado | Tipo | Secop II (`p6dx-8zbt`) | Secop I (`f789-7hwg`) | Descripción |
| :--- | :--- | :--- | :--- | :--- |
| `id` | `string` | `id_del_proceso` / `referencia_del_proceso` | `numero_de_proceso` / `id_proceso` | Identificador único del expediente |
| `processNumber` | `string` | `referencia_del_proceso` | `numero_de_proceso` | Código formal de convocatoria |
| `entity` | `string` | `nombre_entidad` | `nombre_de_la_entidad` | Nombre de la entidad compradora |
| `description` | `string` | `descripci_n_del_procedimiento` | `detalle_del_objeto_a_contratar` | Objeto contractual licitado |
| `budget` | `number` | `precio_base` | `cuantia_proceso` | Cuantía oficial en pesos (COP) |
| `department` | `string` | `departamento_entidad` | `departamento` | Ubicación geográfica normalizada |
| `modality` | `string` | `modalidad_de_contratacion` | `tipo_de_proceso` | Modalidad de selección contractual |
| `status` | `string` | `estado_del_procedimiento` | `estado_del_proceso` | Etapa procesal jurídica |
| `publishDate` | `string` | `fecha_de_publicacion_del` | `fecha_de_cargue_en_el_secop` | Fecha ISO de publicación |
| `deadline` | `string` | `fecha_de_recepcion_de_ofertas` | `fecha_y_hora_de_apertura` | Fecha y hora límite de recepción |
| `source` | `string` | `'Secop II'` | `'Secop I'` | Fuente oficial de origen |
| `url` | `string` | `urlproceso.url` | Construida dinámicamente | Enlace directo al pliego oficial |

### Funciones de Normalización
* [`normalizeSecop2(item)`](file:///c:/Users/Darg/Documents/Volrisk/Herramientas/Radar/index.html#L5173): Limpia caracteres de escape, normaliza valores monetarios nulos o indeterminados y homogeniza fechas.
* [`normalizeSecop1(item)`](file:///c:/Users/Darg/Documents/Volrisk/Herramientas/Radar/index.html#L5226): Parsea cuantías con formato de texto (ej. `$ 150.000.000`), extrae el departamento y construye el enlace al expediente tradicional de Secop I.

---

## 3. Patrones de Rendimiento y Concurrencia

### 3.1. Consultas Federadas con `Promise.allSettled`
A diferencia de `Promise.all` (que falla en cascada si una sola petición es rechazada), Radar utiliza `Promise.allSettled`. Si la API de Secop I tiene tiempo de espera excedido, Radar procesa y despliega inmediatamente los datos de Secop II sin arrojar errores al usuario.

### 3.2. Control de Concurrencia con `AbortController`
Para evitar condiciones de carrera (*race conditions*) cuando el usuario escribe con rapidez o cambia de filtros repetidamente, cada nueva búsqueda cancela la petición HTTP previa en vuelo mediante una señal de aborto (`abortController.abort()`).

### 3.3. Motor de Caché Inteligente en Memoria
El sistema cuenta con un motor de caché de doble capa:
1. **Clave de Caché:** Generada en [`getRadarCacheKey(mode, query)`](file:///c:/Users/Darg/Documents/Volrisk/Herramientas/Radar/index.html#L4848).
2. **Almacenamiento:** Mantiene en memoria las consultas frecuentes con una marca de tiempo.
3. **Respuesta Inmediata:** Si la consulta existe en caché y no ha expirado su TTL (10 minutos), se recupera en 0 milisegundos sin consumir cuota de red de Socrata.

### 3.4. Carga Incremental Paginada (`$offset`)
Para optimizar el uso de memoria, las peticiones iniciales solicitan un bloque de 400 procesos. Si el usuario desea explorar más registros sin rehacer la búsqueda, la función [`loadMoreProcesses()`](file:///c:/Users/Darg/Documents/Volrisk/Herramientas/Radar/index.html#L5621) aplica un `$offset` incremental a la API de Socrata y fusiona los nuevos resultados.

---

## 4. Estado Global y Persistencia Local

Todo el estado del aplicativo se administra de forma reactiva en el objeto global `window` y se sincroniza con `localStorage`:

```mermaid
classDiagram
    class EstadoGlobal {
        +Array allTendersList
        +Array filteredTendersList
        +Object savedTendersMap
        +Array viewedTendersHistory
        +Object comparisonTray
        +Object userProfile
        +Object featureFlags
        +Number sonarInterval
    }
    class LocalStorage {
        +radar_saved_v2
        +radar_viewed_history
        +radar_user_profile
        +radar_comparison_tray_v1
        +radar_tenders_checklists_v1
        +radar_feature_flags
        +radar_auto_refresh_mins
    }
    EstadoGlobal --> LocalStorage : Persistencia en 1-Clic
```

* **`radar_saved_v2`:** Guarda el objeto íntegro de cada licitación marcada en seguimiento. Esto asegura que los pliegos guardados continúen siendo legibles incluso si se consulta offline o si el proceso desaparece del lote reciente de Secop.
* **`radar_tenders_checklists_v1`:** Almacena el estado individual de los 6 ítems habilitantes y los ítems personalizados creados por el usuario para cada licitación.
* **`radar_comparison_tray_v1`:** Mantiene la bandeja de hasta 3 licitaciones seleccionadas para comparación multi-proceso.

---

## 5. Algoritmos y Lógica de Negocio

### 5.1. Termómetro de Urgencia (`getTenderUrgencyBucket`)
Determina la cercanía al cierre calculando la diferencia entre la fecha límite (`deadline`) y la hora del cliente:
$$\Delta t = T_{\text{cierre}} - T_{\text{actual}}$$
* Si $\Delta t < 0$: Proceso cerrado.
* Si $0 \le \Delta t \le 48\text{ horas}$: Cubeta `'urgente'` (alerta crítica con horas restantes).
* Si $48\text{ horas} < \Delta t \le 7\text{ días}$: Cubeta `'semana'` (cierre próximo).
* Si $\Delta t > 7\text{ días}$: Cubeta `'normal'`.

### 5.2. Simulador Financiero AIU (`calculateTenderEconomicsValuesAIU`)
Implementa las fórmulas del régimen de contratación colombiana para contratos con estructura de AIU:
$$\text{AIU Total} = \text{Presupuesto} \times (A\% + I\% + U\%)$$
$$\text{IVA sobre Utilidad} = (\text{Presupuesto} \times U\%) \times 19\%$$
$$\text{Retenciones Estatales} = \text{Presupuesto} \times (\text{ReteFuente}\% + \text{ICA}\%)$$
$$\text{Costos Directos Disponibles} = \text{Presupuesto} - \text{AIU Total} - \text{IVA} - \text{Retenciones}$$

### 5.3. Generador de Eventos de Calendario (.ics & Google Calendar)
* Genera cadenas de fecha compatibles con el estándar **RFC 5545** (UTC con formato `YYYYMMDDTHHmmssZ`).
* Integra la atribución institucional `[vía licitacion.co]` en el título del evento, enlace al expediente y recordatorio 24 horas antes del vencimiento.

### 5.4. Motor de Slugs y Permalinks Semánticos
Convierte títulos y entidades oficiales en rutas semánticas limpias:
* Elimina tildes y caracteres especiales con normalización Unicode (`NFD`).
* Depura palabras vacías en español (*de*, *la*, *en*, *para*, *por*).
* Genera URLs canónicas con fragmento hash: `#/licitacion/:slug` y `#/entidad/:slug`.

---

## 6. Sistema de Feature Flags y Control Freemium

El sistema cuenta con un motor de control de funcionalidades (`initFeatureFlags`, `isFeatureGated`, `isUserLoggedIn`) que permite encender o restringir capacidades según el estado de la cuenta:

* **`FLAG_CATALOG`:** Define las características sujetas a control (`save_to_tracking`, `export_csv`, `smart_alerts`, `aiu_calculator`).
* **Intercepción en Caliente:** Antes de ejecutar una acción protegida (ej. guardar en seguimiento), la función evalúa:
  ```javascript
  if (isFeatureGated('save_to_tracking') && !isUserLoggedIn()) {
      openAuthGateModal('save_tender', 'Guarda licitaciones en seguimiento');
      return;
  }
  ```
* **Simulador de Sesión:** Permite conmutar reactivamente en tiempo de desarrollo o demostración entre el modo *Invitado* y el modo *Registrado*, actualizando la interfaz y las pantallas de bienvenida sin recargar el navegador.
