docs: add README, API reference, and architecture documentation
- README.md: project overview, features, quick start, API overview - docs/API.md: full endpoint reference with examples - docs/ARCHITECTURE.md: system diagram, DB schema, data pipeline, auth flow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
449
docs/ARCHITECTURE.md
Normal file
449
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,449 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user