# Arquitectura - Nexus Autoparts

## Vista General del Sistema

```
+-------------------+       +-------------------+       +-------------------+
|                   |       |                   |       |                   |
|  TecDoc (Apify)   |       |   NHTSA VIN API   |       |   CSV/Excel       |
|  Actor API        |       |   vpic.nhtsa.gov  |       |   (Bodega upload) |
|                   |       |                   |       |                   |
+--------+----------+       +--------+----------+       +--------+----------+
         |                           |                           |
         | download/import           | decode                   | upload
         |                           |                           |
         v                           v                           v
+--------+---------------------------+---------------------------+----------+
|                                                                           |
|                        Flask Server (server.py)                           |
|                        Puerto 5000                                        |
|                                                                           |
|  +-------------+  +-------------+  +-------------+  +-----------------+  |
|  | Auth Module  |  | Catalog API |  | Inventory   |  | Admin CRUD      |  |
|  | (auth.py)    |  | (publico)   |  | (BODEGA)    |  | (import/export) |  |
|  | JWT + bcrypt |  |             |  | CSV mapping |  |                 |  |
|  +------+------+  +------+------+  +------+------+  +--------+--------+  |
|         |                |                |                   |           |
+---------+----------------+----------------+-------------------+-----------+
          |                |                |                   |
          v                v                v                   v
+-------------------------------------------------------------------------+
|                                                                         |
|                    PostgreSQL (nexus_autoparts)                          |
|                    SQLAlchemy text() raw SQL                             |
|                                                                         |
|  +----------+ +----------+ +----------+ +----------+ +---------------+  |
|  | brands   | | models   | | years    | | engines  | | fuel_type     |  |
|  +----+-----+ +----+-----+ +----+-----+ +----+-----+ | drivetrain   |  |
|       |             |            |            |        | transmission |  |
|       +------+------+------------+------------+        +---------------+  |
|              |                                                          |
|              v                                                          |
|  +-----------------------+                                              |
|  | model_year_engine     |  (MYE - tabla central de vehiculos)          |
|  | PK: id_mye            |                                              |
|  | UNIQUE(model,year,    |                                              |
|  |   engine,trim_level)  |                                              |
|  +----------+------------+                                              |
|             |                                                           |
|             |  1:N                                                       |
|             v                                                           |
|  +---------------------+     +---------------------+                   |
|  | vehicle_parts       |     | vehicle_diagrams    |                   |
|  | (12B+ rows)         |     |                     |                   |
|  | mye_id + part_id    |     | mye_id + diagram_id |                   |
|  +----------+----------+     +----------+----------+                   |
|             |                           |                               |
|             v                           v                               |
|  +---------------------+     +---------------------+                   |
|  | parts (1.4M+ OEM)   |     | diagrams            |                   |
|  | oem_part_number      |     | image_path          |                   |
|  | group_id -> groups   |     | group_id -> groups  |                   |
|  +---+------+----------+     +-----+---------------+                   |
|      |      |                       |                                   |
|      |      v                       v                                   |
|      |  +--------------------+  +---------------------+                 |
|      |  | part_groups        |  | diagram_hotspots    |                 |
|      |  | category_id        |  | part_id, coords     |                 |
|      |  +--------+-----------+  +---------------------+                 |
|      |           |                                                      |
|      |           v                                                      |
|      |  +--------------------+                                          |
|      |  | part_categories    |                                          |
|      |  | (arbol jerarquico) |                                          |
|      |  | parent_id -> self  |                                          |
|      |  +--------------------+                                          |
|      |                                                                  |
|      +----------+-----------+                                           |
|                 |           |                                            |
|                 v           v                                            |
|  +---------------------+  +------------------------+                   |
|  | aftermarket_parts   |  | part_cross_references  |                   |
|  | (300K+)             |  | (13M+)                 |                   |
|  | oem_part_id -> parts|  | part_id -> parts       |                   |
|  | manufacturer_id     |  | reference_type         |                   |
|  +----------+----------+  +------------------------+                   |
|             |                                                           |
|             v                                                           |
|  +---------------------+                                               |
|  | manufacturers       |                                               |
|  | type, quality_tier  |                                               |
|  +---------------------+                                               |
|                                                                         |
|  === SaaS Tables ===                                                    |
|                                                                         |
|  +----------+  +----------+  +---------------------+  +-------------+  |
|  | users    |  | roles    |  | warehouse_inventory |  | sessions    |  |
|  | id_user  |  | ADMIN    |  | user_id + part_id   |  | refresh     |  |
|  | id_rol   |  | OWNER    |  | price, stock        |  | token       |  |
|  | email    |  | TALLER   |  | location            |  | expires_at  |  |
|  | pass     |  | BODEGA   |  +---------------------+  +-------------+  |
|  +----------+  +----------+                                             |
|                            +------------------------+                   |
|                            | inventory_uploads      |                   |
|                            | inventory_col_mappings |                   |
|                            +------------------------+                   |
|                                                                         |
|  === Lookup Tables ===                                                  |
|  countries, materials, shapes, position_part, reference_type,           |
|  manufacture_type, quality_tier                                         |
|                                                                         |
+-------------------------------------------------------------------------+

          |                |                |                |
          v                v                v                v
+-------------------+ +----------+ +-------------+ +----------------+
| login.html        | | demo.html| | admin.html  | | bodega.html    |
| tienda.html       | | (catalog)| | (CRUD +     | | (inventory     |
| pos.html          | |          | |  users)     | |  upload/manage) |
| cuentas.html      | |          | |             | |                |
| captura.html      | |          | |             | |                |
+-------------------+ +----------+ +-------------+ +----------------+
        Frontend: HTML/CSS/JS vanilla (sin framework)
        Shared: nav.js, shared.css
```

---

## Database Schema Overview

### Tablas principales y sus relaciones

**Vehiculos (jerarquia):**
```
brands (id_brand, name_brand, country, region)
  |
  +--< models (id_model, brand_id, name_model, body_type)
        |
        +--< model_year_engine (id_mye, model_id, year_id, engine_id, trim_level)
              |     |     |
              |     |     +-- engines (id_engine, name_engine, displacement_cc, cylinders, power_hp)
              |     +-------- years (id_year, year_car)
              |
              +--< vehicle_parts (mye_id, part_id, quantity, position)
              +--< vehicle_diagrams (mye_id, diagram_id)
```

**Partes (jerarquia):**
```
part_categories (id, name, parent_id)   <-- arbol recursivo
  |
  +--< part_groups (id, category_id, name)
        |
        +--< parts (id_part, oem_part_number, name, group_id)
              |
              +--< aftermarket_parts (oem_part_id -> parts, manufacturer_id, part_number)
              +--< part_cross_references (part_id -> parts, cross_reference_number, type)
              +--< vehicle_parts (part_id -> parts, mye_id)
```

**SaaS / Multi-tenant:**
```
roles (id_rol: 1=ADMIN, 2=OWNER, 3=TALLER, 4=BODEGA)
  |
  +--< users (id_user, email, pass, id_rol, business_name, is_active)
        |
        +--< sessions (refresh_token, expires_at)
        +--< warehouse_inventory (user_id, part_id, price, stock, location)
        +--< inventory_uploads (user_id, filename, status, rows_imported)
        +--< inventory_column_mappings (user_id, mapping JSON)
```

### Convenciones de nombres en PostgreSQL

| Tabla | PK | Naming pattern |
|-------|-----|---------------|
| brands | `id_brand` | `name_brand` |
| models | `id_model` | `name_model` |
| years | `id_year` | `year_car` |
| engines | `id_engine` | `name_engine` |
| model_year_engine | `id_mye` | Tabla pivote central |
| parts | `id_part` | `name_part`, `oem_part_number` |
| part_categories | `id_part_category` | `name_part_category` |
| part_groups | `id_part_group` | `name_part_group` |
| manufacturers | `id_manufacture` | `name_manufacture` |

---

## TecDoc Data Pipeline

Pipeline de 3 fases para importar datos de TecDoc (el catalogo europeo de autopartes).

```
Fase 1: DOWNLOAD                 Fase 2: IMPORT                    Fase 3: LINK
(import_tecdoc.py download)      (import_tecdoc.py import)         (link_vehicle_parts.py)

+------------------+             +------------------+              +------------------+
| Apify TecDoc     |             | JSON files       |              | parts +          |
| Actor API        |             | data/tecdoc/     |              | model_year_engine|
| $69/month        |             |                  |              |                  |
| HTTP 201 = run   |             | manufacturers/   |              | Genera links     |
|                  |  -------->  | models/          |  --------->  | masivos en       |
| typeId=1 (cars)  |   JSON      | vehicles/        |   INSERT     | vehicle_parts    |
| langId=4 (EN)    |   files     |                  |   partes +   | (12B+ filas)     |
| countryId=153(MX)|             | Resumable:       |   vehiculos  |                  |
+------------------+             | skips existing   |              +------------------+
                                 +------------------+
```

### Scripts adicionales

- **`import_tecdoc_parts.py`**: Importa partes OEM y aftermarket desde TecDoc
- **`import_live.py`**: Importa datos en tiempo real (sin bajar a JSON primero)
- **`migrate_aftermarket.py`**: Normaliza datos aftermarket a la estructura con `quality_tier`, `manufacturers`

### Filtros de importacion

- Se descartan variantes regionales (e.g., "TOYOTA (GAC)")
- `countryFilterId=153` limita a vehiculos vendidos en Mexico
- El script es resumable: si un JSON ya existe en `data/tecdoc/`, se salta

---

## Auth Flow

Flujo de autenticacion basado en JWT con refresh tokens.

```
  Cliente                        Servidor                      PostgreSQL
    |                               |                              |
    |  POST /api/auth/register      |                              |
    |  {name, email, pass, role}    |                              |
    |------------------------------>|  bcrypt.hash(pass)           |
    |                               |----------------------------->|
    |                               |  INSERT users (is_active=F)  |
    |  201 "pending activation"     |                              |
    |<------------------------------|                              |
    |                               |                              |
    |  (Admin activa la cuenta)     |                              |
    |                               |                              |
    |  POST /api/auth/login         |                              |
    |  {email, password}            |                              |
    |------------------------------>|  SELECT users WHERE email    |
    |                               |<-----------------------------|
    |                               |  bcrypt.check(pass)          |
    |                               |  Verify is_active=true       |
    |                               |                              |
    |                               |  jwt.encode(user_id, role,   |
    |                               |    business_name, exp=15min) |
    |                               |                              |
    |                               |  secrets.token_urlsafe(48)   |
    |                               |  INSERT sessions             |
    |                               |----------------------------->|
    |                               |                              |
    |  {access_token, refresh_token,|                              |
    |   user: {id, name, role}}     |                              |
    |<------------------------------|                              |
    |                               |                              |
    |  GET /api/... (protected)     |                              |
    |  Authorization: Bearer <AT>   |                              |
    |------------------------------>|  jwt.decode(AT)              |
    |                               |  Check role in allowed_roles |
    |                               |  Set g.user = {user_id, role}|
    |  200 {data}                   |                              |
    |<------------------------------|                              |
    |                               |                              |
    |  (Access token expires)       |                              |
    |                               |                              |
    |  POST /api/auth/refresh       |                              |
    |  {refresh_token}              |                              |
    |------------------------------>|  SELECT sessions             |
    |                               |<-----------------------------|
    |                               |  Check expires_at > now()    |
    |                               |  jwt.encode(new access_token)|
    |  {access_token}               |                              |
    |<------------------------------|                              |
```

### Roles y permisos

| Rol | ID | Permisos |
|-----|----|----------|
| `ADMIN` | 1 | Todo: gestionar usuarios, catalogo, inventario |
| `OWNER` | 2 | Gestionar usuarios, ver estadisticas |
| `TALLER` | 3 | Consultar catalogo, ver disponibilidad en bodegas |
| `BODEGA` | 4 | Gestionar inventario propio, subir CSV/Excel |

### JWT Token payload

```json
{
  "user_id": 1,
  "role": "TALLER",
  "business_name": "Taller Perez",
  "type": "access",
  "exp": 1742300000,
  "iat": 1742299100
}
```

---

## Inventory Flow (Bodega)

Flujo para que una bodega suba su inventario via CSV/Excel.

```
  Bodega (Browser)               Servidor                      PostgreSQL
    |                               |                              |
    |  1. PUT /api/inventory/mapping|                              |
    |  {part_number: "NUM_PARTE",   |                              |
    |   price: "PRECIO",            |                              |
    |   stock: "EXISTENCIA",        |  UPSERT inventory_col_map   |
    |   location: "ALMACEN"}        |----------------------------->|
    |<------------------------------|                              |
    |                               |                              |
    |  2. POST /api/inventory/upload|                              |
    |  multipart/form-data: file    |                              |
    |------------------------------>|                              |
    |                               |  a) Read mapping             |
    |                               |<-----------------------------|
    |                               |                              |
    |                               |  b) Parse CSV/Excel          |
    |                               |     (openpyxl or csv module) |
    |                               |                              |
    |                               |  c) For each row:            |
    |                               |     - Extract part_number    |
    |                               |       using mapping          |
    |                               |     - Find part by OEM #     |
    |                               |     - If not found, try      |
    |                               |       aftermarket_parts      |
    |                               |     - UPSERT warehouse_inv   |
    |                               |----------------------------->|
    |                               |                              |
    |  {imported: 450, errors: 12}  |  d) Update inventory_uploads |
    |<------------------------------|----------------------------->|
```

### Tabla `warehouse_inventory`

```sql
warehouse_inventory (
    id_inventory    BIGSERIAL PRIMARY KEY,
    user_id         INTEGER REFERENCES users,
    part_id         INTEGER REFERENCES parts,
    price           NUMERIC(12,2),
    stock_quantity  INTEGER DEFAULT 0,
    warehouse_location VARCHAR(100) DEFAULT 'Principal',
    updated_at      TIMESTAMP,
    UNIQUE(user_id, part_id, warehouse_location)
)
```

El UNIQUE constraint permite que una bodega tenga la misma parte en multiples ubicaciones (e.g., "Principal", "Sucursal Norte").

---

## Aftermarket Linking

Como se relacionan partes OEM con alternativas aftermarket y cross-references.

```
                    +--------------------+
                    | parts              |
                    | (OEM catalog)      |
                    | id_part: 500       |
                    | oem_part_number:   |
                    |  "04152-YZZA1"     |
                    +--------+-----------+
                             |
              +--------------+--------------+
              |                             |
              v                             v
+----------------------------+  +----------------------------+
| aftermarket_parts          |  | part_cross_references      |
| (alternativas)             |  | (numeros alternos)         |
|                            |  |                            |
| oem_part_id: 500           |  | part_id: 500               |
| manufacturer_id: -> DENSO  |  | cross_reference_number:    |
| part_number: "DXP-1234"   |  |   "90915-YZZF1"           |
| quality_tier: "premium"    |  | reference_type:            |
| price_usd: 12.50           |  |   "oem_alternate"         |
| warranty_months: 24         |  | source: "TecDoc"          |
+----------------------------+  +----------------------------+
```

### Tipos de cross-reference

| Tipo | Descripcion |
|------|-------------|
| `oem_alternate` | Otro numero OEM para la misma parte (mismo fabricante) |
| `supersession` | La parte fue reemplazada por un numero nuevo |
| `interchange` | Numero de un competidor que es equivalente |
| `competitor` | Referencia cruzada a otra marca |

### Flujo de busqueda por numero de parte

Cuando un taller busca un numero de parte, el sistema busca en 3 lugares:

1. **`parts.oem_part_number`** - Match directo OEM
2. **`aftermarket_parts.part_number`** - Match en parte aftermarket, retorna el OEM original
3. **`part_cross_references.cross_reference_number`** - Match en cross-ref, retorna el OEM original

Esto permite que el taller encuentre la parte sin importar que numero use (OEM, aftermarket, o numero alterno).

### Calidad (quality_tier)

| Tier | Descripcion | Ejemplo |
|------|-------------|---------|
| `economy` | Precio bajo, calidad basica | Marcas genericas |
| `standard` | Calidad media, buen balance | Monroe, Moog |
| `premium` | Calidad alta, cercana a OEM | Denso, Bosch |

---

## Stack Frontend

El frontend es HTML/CSS/JS vanilla sin framework. Cada pagina es independiente.

```
dashboard/
  +-- shared.css          # Estilos compartidos (colores, layout, cards)
  +-- nav.js              # Navegacion compartida (inyecta sidebar/header)
  +-- login.html + .css + .js
  +-- demo.html                  # Catalogo publico
  +-- admin.html + .js           # Panel admin (CRUD, users, import/export)
  +-- bodega.html + .css + .js   # Inventory management
  +-- tienda.html + .css + .js   # Store view para talleres
  +-- pos.html + .css + .js      # Point of sale
  +-- captura.html + .css + .js  # Captura de partes
  +-- cuentas.html + .css + .js  # Gestion de cuentas
  +-- dashboard.js               # Logica del catalogo principal
  +-- enhanced-search.js         # Busqueda avanzada
```

### Patron de comunicacion con API

Todas las paginas usan `fetch()` con el patron:

```javascript
const token = localStorage.getItem('access_token');
const res = await fetch('/api/...', {
  headers: { 'Authorization': `Bearer ${token}` }
});
const data = await res.json();
```

### Nota sobre NUMERIC de PostgreSQL

PostgreSQL retorna valores `NUMERIC` como strings. Todas las funciones de formato en JS usan `parseFloat()` para convertir antes de mostrar.

---

## Infraestructura y Monitoreo (2026-04)

### Servicios Systemd

| Servicio | Descripcion | Puerto |
|----------|-------------|--------|
| `nexus-pos.service` | Gunicorn POS (Flask) | 5001 |
| `nexus.service` | Dashboard (Flask) | 5000 |
| `nexus-quart.service` | Hypercorn async catalog | 5002 |
| `nexus-celery.service` | Celery workers | — |
| `nexus-mv-refresh.timer` | Refresh MV diario 03:00 UTC | — |
| `nexus-cache-warm.timer` | Cache warming diario 04:00 UTC | — |

### Monitoreo (Prometheus + Grafana)

- **Prometheus** :9090 — métricas de sistema, PostgreSQL, Redis
- **Grafana** :3001 — dashboards visuales (login admin/nexus2026)
- Exporters: node-exporter, postgres-exporter, redis-exporter

### Stubs de Integraciones de Negocio

- **BNPL** (`bnpl_bp.py`): APLAZO, Kueski, Clip
- **ERP Sync** (`erp_bp.py`): Aspel SAE, CONTPAQi, SAP B1, Odoo
- **WhatsApp Meta Cloud** (`whatsapp_cloud_bp.py`): reemplazo escalable de Baileys
- **Supplier Portal** (`supplier_portal_bp.py`): demanda por zona y top partes

### PWA

- Service Worker (`pos/static/pwa/sw.js`) con cache-first para assets
- Install prompt (`pos/static/js/pwa-install.js`) captura `beforeinstallprompt`
- Manifest registrado en 14 templates POS

### Tests E2E

- Playwright + Chromium
- Primer smoke test: login page loads and rejects invalid credentials
