- 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>
22 KiB
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 TecDocimport_live.py: Importa datos en tiempo real (sin bajar a JSON primero)migrate_aftermarket.py: Normaliza datos aftermarket a la estructura conquality_tier,manufacturers
Filtros de importacion
- Se descartan variantes regionales (e.g., "TOYOTA (GAC)")
countryFilterId=153limita 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
{
"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
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:
parts.oem_part_number- Match directo OEMaftermarket_parts.part_number- Match en parte aftermarket, retorna el OEM originalpart_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:
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.