- 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>
1474 lines
27 KiB
Markdown
1474 lines
27 KiB
Markdown
# API Reference - Nexus Autoparts
|
|
|
|
Documentacion completa de la API REST del sistema Nexus Autoparts.
|
|
|
|
## Base URL
|
|
|
|
```
|
|
http://localhost:5000
|
|
```
|
|
|
|
## Autenticacion
|
|
|
|
Los endpoints protegidos requieren un header `Authorization: Bearer <access_token>`.
|
|
Los access tokens tienen una duracion de 15 minutos. Usa el endpoint `/api/auth/refresh` para obtener uno nuevo.
|
|
|
|
---
|
|
|
|
## 1. Auth
|
|
|
|
### POST /api/auth/register
|
|
|
|
Registra un nuevo usuario. La cuenta inicia inactiva (requiere activacion por ADMIN).
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Request body:**
|
|
```json
|
|
{
|
|
"name": "Juan Perez",
|
|
"email": "juan@taller.com",
|
|
"password": "secreto123",
|
|
"role": "TALLER",
|
|
"business_name": "Taller Perez",
|
|
"phone": "555-1234",
|
|
"address": "Calle 1 #123"
|
|
}
|
|
```
|
|
|
|
| Campo | Tipo | Requerido | Descripcion |
|
|
|-------|------|-----------|-------------|
|
|
| name | string | Si | Nombre del usuario |
|
|
| email | string | Si | Email (unico) |
|
|
| password | string | Si | Password (se hashea con bcrypt) |
|
|
| role | string | Si | `TALLER` o `BODEGA` |
|
|
| business_name | string | Si | Nombre del negocio |
|
|
| phone | string | No | Telefono |
|
|
| address | string | No | Direccion |
|
|
|
|
**Response 201:**
|
|
```json
|
|
{
|
|
"message": "Registration successful. Account pending activation."
|
|
}
|
|
```
|
|
|
|
**Response 409:**
|
|
```json
|
|
{
|
|
"error": "Email already registered"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### POST /api/auth/login
|
|
|
|
Autentica un usuario y retorna access + refresh tokens.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Request body:**
|
|
```json
|
|
{
|
|
"email": "juan@taller.com",
|
|
"password": "secreto123"
|
|
}
|
|
```
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"access_token": "eyJhbGci...",
|
|
"refresh_token": "abc123def456...",
|
|
"user": {
|
|
"id": 1,
|
|
"name": "Juan Perez",
|
|
"role": "TALLER",
|
|
"business_name": "Taller Perez"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Errores:**
|
|
- `401` - Email o password invalido
|
|
- `403` - Cuenta no activa
|
|
|
|
---
|
|
|
|
### POST /api/auth/refresh
|
|
|
|
Renueva un access token usando un refresh token valido.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Request body:**
|
|
```json
|
|
{
|
|
"refresh_token": "abc123def456..."
|
|
}
|
|
```
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"access_token": "eyJhbGci..."
|
|
}
|
|
```
|
|
|
|
**Errores:**
|
|
- `401` - Refresh token invalido o expirado
|
|
|
|
---
|
|
|
|
### GET /api/auth/me
|
|
|
|
Retorna la informacion del usuario autenticado (extraida del JWT).
|
|
|
|
**Auth:** Cualquier rol autenticado
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"user_id": 1,
|
|
"role": "TALLER",
|
|
"business_name": "Taller Perez"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Admin - User Management
|
|
|
|
### GET /api/admin/users
|
|
|
|
Lista todos los usuarios registrados.
|
|
|
|
**Auth:** `ADMIN`, `OWNER`
|
|
|
|
**Response 200:**
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"name": "Juan Perez",
|
|
"email": "juan@taller.com",
|
|
"business_name": "Taller Perez",
|
|
"phone": "555-1234",
|
|
"is_active": true,
|
|
"created_at": "2026-03-01T10:00:00",
|
|
"last_login": "2026-03-18T08:30:00",
|
|
"role": "TALLER"
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### PUT /api/admin/users/{user_id}/activate
|
|
|
|
Activa o desactiva un usuario.
|
|
|
|
**Auth:** `ADMIN`, `OWNER`
|
|
|
|
**Request body:**
|
|
```json
|
|
{
|
|
"is_active": true
|
|
}
|
|
```
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"message": "User updated",
|
|
"is_active": true
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/admin/stats
|
|
|
|
Estadisticas generales del catalogo. Usa `pg_class.reltuples` para tablas grandes (performance).
|
|
|
|
**Auth:** No requerida (publico)
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"categories": 10,
|
|
"groups": 150,
|
|
"manufacturers": 200,
|
|
"parts": 1400000,
|
|
"aftermarket": 300000,
|
|
"fitment": 12000000000
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Catalog - Brands, Models, Years, Engines
|
|
|
|
### GET /api/brands
|
|
|
|
Lista todas las marcas que tienen vehiculos en la base de datos.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| detailed | bool | Si `true`, incluye `model_count` y `vehicle_count` |
|
|
| region | int | Bitmask de region para filtrar marcas |
|
|
|
|
**Response 200 (simple):**
|
|
```json
|
|
["ACURA", "AUDI", "BMW", "CHEVROLET", "FORD", "HONDA", "TOYOTA"]
|
|
```
|
|
|
|
**Response 200 (detailed=true):**
|
|
```json
|
|
[
|
|
{
|
|
"name": "TOYOTA",
|
|
"model_count": 85,
|
|
"vehicle_count": 4200
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/models
|
|
|
|
Lista modelos, opcionalmente filtrados por marca.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| brand | string | Nombre de la marca (ILIKE) |
|
|
| detailed | bool | Si `true` con brand, incluye year_min/max, vehicle_count, engine_count |
|
|
|
|
**Response 200 (simple):**
|
|
```json
|
|
["4RUNNER", "CAMRY", "COROLLA", "HIGHLANDER", "RAV4"]
|
|
```
|
|
|
|
**Response 200 (detailed=true&brand=TOYOTA):**
|
|
```json
|
|
[
|
|
{
|
|
"name": "CAMRY",
|
|
"year_min": 1987,
|
|
"year_max": 2026,
|
|
"year_count": 40,
|
|
"vehicle_count": 320,
|
|
"engine_count": 15
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/years
|
|
|
|
Lista anos disponibles, opcionalmente filtrados.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| brand | string | Filtrar por marca |
|
|
| model | string | Filtrar por modelo |
|
|
|
|
**Response 200:**
|
|
```json
|
|
[2026, 2025, 2024, 2023, 2022]
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/engines
|
|
|
|
Lista motores disponibles, opcionalmente filtrados.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| brand | string | Filtrar por marca |
|
|
| model | string | Filtrar por modelo |
|
|
| year | int | Filtrar por ano |
|
|
|
|
**Response 200:**
|
|
```json
|
|
["1.8L I4 Gas", "2.0L I4 Hybrid", "2.5L I4 Gas"]
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/vehicles
|
|
|
|
Busqueda de vehiculos con filtros y paginacion.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Default | Descripcion |
|
|
|-------|------|---------|-------------|
|
|
| brand | string | - | Filtrar por marca |
|
|
| model | string | - | Filtrar por modelo |
|
|
| year | int | - | Filtrar por ano |
|
|
| engine | string | - | Filtrar por motor |
|
|
| page | int | 1 | Pagina |
|
|
| per_page | int | 50 | Items por pagina (max 100) |
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"brand": "TOYOTA",
|
|
"model": "CAMRY",
|
|
"year": 2020,
|
|
"engine": "2.5L I4 Gas",
|
|
"power_hp": 203,
|
|
"torque_nm": 250,
|
|
"displacement_cc": 2487,
|
|
"cylinders": 4,
|
|
"fuel_type": "gasoline",
|
|
"trim_level": "LE",
|
|
"drivetrain": "FWD",
|
|
"transmission": "automatic"
|
|
}
|
|
],
|
|
"pagination": {
|
|
"page": 1,
|
|
"per_page": 50,
|
|
"total": 320,
|
|
"total_pages": 7
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/model-year-engine
|
|
|
|
Similar a `/api/vehicles` pero retorna `id` (mye_id) para navegacion al catalogo de partes.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:** Igual que `/api/vehicles`, mas `with_parts` (bool) para filtrar solo vehiculos con partes catalogadas.
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"id": 12345,
|
|
"brand": "TOYOTA",
|
|
"model": "CAMRY",
|
|
"year": 2020,
|
|
"engine": "2.5L I4 Gas",
|
|
"trim_level": "LE",
|
|
"drivetrain": "FWD",
|
|
"transmission": "automatic"
|
|
}
|
|
],
|
|
"pagination": { "page": 1, "per_page": 50, "total": 120, "total_pages": 3 }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/catalog/stats
|
|
|
|
Estadisticas basicas del catalogo.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"brands": 107,
|
|
"models": 10923,
|
|
"vehicles": 82456,
|
|
"parts": 1400000
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Parts Catalog
|
|
|
|
### GET /api/categories
|
|
|
|
Lista categorias de partes en estructura de arbol.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Response 200:**
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"name": "Engine",
|
|
"name_es": "Motor",
|
|
"slug": "engine",
|
|
"icon_name": "engine",
|
|
"display_order": 1,
|
|
"children": [
|
|
{
|
|
"id": 5,
|
|
"name": "Engine Electrical",
|
|
"name_es": "Electrico del Motor",
|
|
"slug": "engine-electrical",
|
|
"icon_name": null,
|
|
"display_order": 2,
|
|
"children": []
|
|
}
|
|
]
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/categories/{category_id}/groups
|
|
|
|
Lista grupos (subcategorias) dentro de una categoria.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Response 200:**
|
|
```json
|
|
[
|
|
{
|
|
"id": 10,
|
|
"name": "Oil Filter",
|
|
"name_es": "Filtro de Aceite",
|
|
"slug": "oil-filter",
|
|
"display_order": 1
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/parts
|
|
|
|
Lista partes con paginacion y filtros.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| group_id | int | Filtrar por grupo |
|
|
| category_id | int | Filtrar por categoria |
|
|
| search | string | Busqueda por nombre o numero OEM (ILIKE) |
|
|
| page | int | Pagina (default 1) |
|
|
| per_page | int | Items por pagina (max 100) |
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"id": 500,
|
|
"oem_part_number": "04152-YZZA1",
|
|
"name": "Oil Filter",
|
|
"name_es": "Filtro de Aceite",
|
|
"group_id": 10,
|
|
"group_name": "Oil Filters",
|
|
"category_name": "Engine",
|
|
"image_url": "/static/parts_images/abc.jpg"
|
|
}
|
|
],
|
|
"pagination": { "page": 1, "per_page": 50, "total": 200, "total_pages": 4 }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/parts/{part_id}
|
|
|
|
Detalle de una parte especifica.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"id": 500,
|
|
"oem_part_number": "04152-YZZA1",
|
|
"name": "Oil Filter",
|
|
"name_es": "Filtro de Aceite",
|
|
"description": "Engine oil filter element",
|
|
"description_es": "Elemento de filtro de aceite del motor",
|
|
"group_id": 10,
|
|
"group_name": "Oil Filters",
|
|
"group_name_es": "Filtros de Aceite",
|
|
"category_id": 1,
|
|
"category_name": "Engine",
|
|
"category_name_es": "Motor",
|
|
"image_url": null
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/vehicles/{mye_id}/categories
|
|
|
|
Categorias de partes disponibles para un vehiculo especifico.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Response 200:**
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"name": "Engine",
|
|
"name_es": "Motor",
|
|
"slug": "engine",
|
|
"icon_name": "engine",
|
|
"display_order": 1
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/vehicles/{mye_id}/groups
|
|
|
|
Grupos de partes disponibles para un vehiculo, opcionalmente filtrado por categoria.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| category_id | int | Filtrar por categoria |
|
|
|
|
**Response 200:**
|
|
```json
|
|
[
|
|
{
|
|
"id": 10,
|
|
"name": "Oil Filters",
|
|
"name_es": "Filtros de Aceite",
|
|
"slug": "oil-filters",
|
|
"display_order": 1,
|
|
"parts_count": 5
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/vehicles/{mye_id}/parts
|
|
|
|
Partes para un vehiculo especifico con paginacion.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| category_id | int | Filtrar por categoria |
|
|
| group_id | int | Filtrar por grupo |
|
|
| page | int | Pagina (default 1) |
|
|
| per_page | int | Items por pagina (max 100) |
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"id": 500,
|
|
"oem_part_number": "04152-YZZA1",
|
|
"name": "Oil Filter",
|
|
"name_es": "Filtro de Aceite",
|
|
"quantity_required": 1,
|
|
"position": "front",
|
|
"category_name": "Engine",
|
|
"group_name": "Oil Filters",
|
|
"image_url": null
|
|
}
|
|
],
|
|
"pagination": { "page": 1, "per_page": 50, "total": 45, "total_pages": 1 }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Cross-References and Aftermarket
|
|
|
|
### GET /api/manufacturers
|
|
|
|
Lista fabricantes (OEM y aftermarket).
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| type | string | `oem`, `aftermarket`, `remanufactured` |
|
|
| quality_tier | string | `economy`, `standard`, `premium`, `oem` |
|
|
|
|
**Response 200:**
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"name": "DENSO",
|
|
"type": "aftermarket",
|
|
"quality_tier": "premium",
|
|
"country": "Japan",
|
|
"logo_url": null,
|
|
"website": "https://denso.com"
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/parts/{part_id}/alternatives
|
|
|
|
Alternativas aftermarket para una parte OEM.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| quality_tier | string | Filtrar por calidad |
|
|
| manufacturer_id | int | Filtrar por fabricante |
|
|
|
|
**Response 200:**
|
|
```json
|
|
[
|
|
{
|
|
"id": 100,
|
|
"part_number": "XP-1234",
|
|
"name": "Premium Oil Filter",
|
|
"name_es": "Filtro de Aceite Premium",
|
|
"manufacturer_name": "DENSO",
|
|
"manufacturer_id": 1,
|
|
"quality_tier": "premium",
|
|
"price_usd": 12.50,
|
|
"warranty_months": 24,
|
|
"in_stock": null
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/parts/{part_id}/cross-references
|
|
|
|
Cross-references (numeros alternos) para una parte.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Response 200:**
|
|
```json
|
|
[
|
|
{
|
|
"id": 50,
|
|
"cross_reference_number": "90915-YZZF1",
|
|
"reference_type": "oem_alternate",
|
|
"source": "TecDoc",
|
|
"notes": null
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/parts/{part_id}/availability
|
|
|
|
Disponibilidad en bodegas activas que tienen la parte en stock.
|
|
|
|
**Auth:** `TALLER`, `ADMIN`, `OWNER`
|
|
|
|
**Response 200:**
|
|
```json
|
|
[
|
|
{
|
|
"bodega": "Refacciones del Norte",
|
|
"price": 125.50,
|
|
"stock": 15,
|
|
"location": "Principal",
|
|
"updated_at": "2026-03-18T10:00:00"
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/parts/{part_id}/aftermarket
|
|
|
|
Alternativas aftermarket y cross-references combinadas (endpoint publico).
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"alternatives": [
|
|
{
|
|
"id": 100,
|
|
"part_number": "XP-1234",
|
|
"name": "Premium Oil Filter",
|
|
"manufacturer": "DENSO",
|
|
"quality_tier": "premium",
|
|
"price": 12.50,
|
|
"source": "aftermarket"
|
|
}
|
|
],
|
|
"cross_references": [
|
|
{
|
|
"number": "90915-YZZF1",
|
|
"source": "TecDoc",
|
|
"notes": null
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/aftermarket
|
|
|
|
Lista partes aftermarket con paginacion y filtros.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| manufacturer_id | int | Filtrar por fabricante |
|
|
| quality_tier | string | Filtrar por calidad |
|
|
| search | string | Buscar por nombre, part number o OEM number |
|
|
| page | int | Pagina (default 1) |
|
|
| per_page | int | Items por pagina (max 100) |
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"id": 100,
|
|
"part_number": "XP-1234",
|
|
"name": "Premium Oil Filter",
|
|
"oem_part_number": "04152-YZZA1",
|
|
"manufacturer_name": "DENSO",
|
|
"quality_tier": "premium",
|
|
"price_usd": 12.50
|
|
}
|
|
],
|
|
"pagination": { "page": 1, "per_page": 50, "total": 300000, "total_pages": 6000 }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/search/part-number/{part_number}
|
|
|
|
Busqueda por numero de parte en OEM, aftermarket y cross-references.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Response 200:**
|
|
```json
|
|
[
|
|
{
|
|
"id": 500,
|
|
"oem_part_number": "04152-YZZA1",
|
|
"name": "Oil Filter",
|
|
"name_es": "Filtro de Aceite",
|
|
"match_type": "oem",
|
|
"matched_number": "04152-YZZA1"
|
|
},
|
|
{
|
|
"id": 500,
|
|
"oem_part_number": "04152-YZZA1",
|
|
"name": "Oil Filter",
|
|
"name_es": "Filtro de Aceite",
|
|
"match_type": "cross_reference",
|
|
"matched_number": "90915-YZZF1"
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Search
|
|
|
|
### GET /api/search
|
|
|
|
Busqueda combinada inteligente. Detecta automaticamente vehiculo + parte en el query.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Default | Descripcion |
|
|
|-------|------|---------|-------------|
|
|
| q | string | (requerido) | Texto de busqueda |
|
|
| type | string | `all` | `all`, `parts`, `vehicles` |
|
|
| category_id | int | - | Filtrar partes por categoria |
|
|
| limit | int | 50 | Limite de resultados |
|
|
| offset | int | 0 | Offset para paginacion |
|
|
|
|
**Ejemplo:** `GET /api/search?q=Toyota Corolla 2020 oil filter`
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"parts": [
|
|
{
|
|
"id": 500,
|
|
"oem_part_number": "04152-YZZA1",
|
|
"name": "Oil Filter",
|
|
"name_es": null,
|
|
"image_url": null,
|
|
"group_name": "Oil Filters",
|
|
"category_name": "Engine",
|
|
"match_type": "oem"
|
|
}
|
|
],
|
|
"vehicles": [
|
|
{
|
|
"id": 12345,
|
|
"brand": "TOYOTA",
|
|
"model": "COROLLA",
|
|
"year": 2020,
|
|
"engine": "1.8L I4 Gas"
|
|
}
|
|
],
|
|
"vehicle_parts": [
|
|
{
|
|
"id": 500,
|
|
"oem_part_number": "04152-YZZA1",
|
|
"name": "Oil Filter",
|
|
"group_name": "Oil Filters",
|
|
"category_name": "Engine",
|
|
"quantity": 1,
|
|
"position": null,
|
|
"match_type": "vehicle_part"
|
|
}
|
|
],
|
|
"matched_vehicle": {
|
|
"id": 12345,
|
|
"brand": "TOYOTA",
|
|
"model": "COROLLA",
|
|
"year": 2020,
|
|
"engine": "1.8L I4 Gas"
|
|
},
|
|
"total_count": 5
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/search/parts
|
|
|
|
Busqueda full-text en partes usando PostgreSQL `tsvector`.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| q | string | (requerido) Texto de busqueda |
|
|
| category_id | int | Filtrar por categoria |
|
|
| group_id | int | Filtrar por grupo |
|
|
| page | int | Pagina (default 1) |
|
|
| per_page | int | Items por pagina (max 100) |
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"id": 500,
|
|
"oem_part_number": "04152-YZZA1",
|
|
"name": "Oil Filter",
|
|
"name_es": "Filtro de Aceite",
|
|
"description": "Engine oil filter",
|
|
"group_name": "Oil Filters",
|
|
"category_name": "Engine",
|
|
"rank": 0.85
|
|
}
|
|
],
|
|
"pagination": { "page": 1, "per_page": 50, "total": 25, "total_pages": 1 }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 7. VIN Decoder
|
|
|
|
### GET /api/vin/decode/{vin}
|
|
|
|
Decodifica un VIN de 17 caracteres via la API de NHTSA. Cacheado por 30 dias.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"vin": "1HGBH41JXMN109186",
|
|
"make": "HONDA",
|
|
"model": "CIVIC",
|
|
"year": 2021,
|
|
"engine_info": {
|
|
"configuration": "In-Line",
|
|
"cylinders": 4,
|
|
"displacement_l": 2.0,
|
|
"fuel_type": "Gasoline"
|
|
},
|
|
"body_class": "Sedan/Saloon",
|
|
"drive_type": "FWD",
|
|
"matched_vehicle": {
|
|
"mye_id": 5678,
|
|
"brand": "HONDA",
|
|
"model": "CIVIC",
|
|
"year": 2021,
|
|
"engine": "2.0L I4 Gas"
|
|
},
|
|
"cached": false
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/vin/{vin}/parts
|
|
|
|
Retorna partes organizadas por categoria para un VIN previamente decodificado.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| category_id | int | Filtrar por categoria |
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"vin": "1HGBH41JXMN109186",
|
|
"vehicle_info": {
|
|
"vin": "1HGBH41JXMN109186",
|
|
"make": "HONDA",
|
|
"model": "CIVIC",
|
|
"year": 2021,
|
|
"mye_id": 5678
|
|
},
|
|
"categories": [
|
|
{
|
|
"id": 1,
|
|
"name": "Engine",
|
|
"name_es": "Motor",
|
|
"parts": [
|
|
{
|
|
"id": 500,
|
|
"oem_part_number": "15400-RTA-004",
|
|
"name": "Oil Filter",
|
|
"name_es": "Filtro de Aceite",
|
|
"group_name": "Oil Filters",
|
|
"quantity_required": 1,
|
|
"position": null
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/vin/{vin}/match
|
|
|
|
Vincula manualmente un VIN a un vehiculo (model_year_engine).
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| mye_id | int | (requerido) ID del model_year_engine |
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"vin": "1HGBH41JXMN109186",
|
|
"mye_id": 5678
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Diagrams
|
|
|
|
### GET /api/diagrams
|
|
|
|
Lista diagramas, opcionalmente filtrados por grupo.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| group_id | int | Filtrar por grupo de partes |
|
|
|
|
**Response 200:**
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"name": "Front Suspension Assembly",
|
|
"name_es": "Ensamble de Suspension Delantera",
|
|
"group_id": 20,
|
|
"group_name": "Suspension",
|
|
"thumbnail_path": "/static/diagrams/thumb_001.jpg",
|
|
"display_order": 1
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/diagrams/{diagram_id}
|
|
|
|
Detalle de un diagrama con hotspots.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"name": "Front Suspension Assembly",
|
|
"name_es": "Ensamble de Suspension Delantera",
|
|
"group_id": 20,
|
|
"group_name": "Suspension",
|
|
"image_path": "static/diagrams/moog/front_suspension.png",
|
|
"image_url": "/static/diagrams/moog/front_suspension.png",
|
|
"svg_content": null,
|
|
"width": null,
|
|
"height": null,
|
|
"hotspots": [
|
|
{
|
|
"id": 10,
|
|
"part_id": 500,
|
|
"callout_number": 1,
|
|
"label": null,
|
|
"shape": "circle",
|
|
"coords": "150,200,25",
|
|
"color": null,
|
|
"part_name": "Ball Joint",
|
|
"part_number": "K500123"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/vehicles/{mye_id}/diagrams
|
|
|
|
Diagramas disponibles para un vehiculo especifico.
|
|
|
|
**Auth:** No requerida
|
|
|
|
---
|
|
|
|
### GET /api/vehicles/{mye_id}/diagrams/by-category
|
|
|
|
Diagramas agrupados por categoria para un vehiculo.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| category_id | int | Filtrar por categoria |
|
|
|
|
---
|
|
|
|
### GET /api/diagrams/{diagram_id}/hotspots
|
|
|
|
Lista hotspots de un diagrama.
|
|
|
|
**Auth:** No requerida
|
|
|
|
---
|
|
|
|
### GET /api/diagrams/{diagram_id}/parts
|
|
|
|
Partes asociadas a un diagrama (con cross-references).
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| mye_id | int | Filtrar por vehiculo especifico |
|
|
|
|
---
|
|
|
|
### GET /api/diagrams/search
|
|
|
|
Busqueda de diagramas por nombre o vehiculo.
|
|
|
|
**Auth:** No requerida
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Descripcion |
|
|
|-------|------|-------------|
|
|
| q | string | Texto de busqueda en nombre |
|
|
| brand | string | Filtrar por marca |
|
|
| model | string | Filtrar por modelo |
|
|
|
|
---
|
|
|
|
## 9. Inventory (BODEGA)
|
|
|
|
### GET /api/inventory/mapping
|
|
|
|
Retorna el mapeo de columnas configurado para el usuario actual.
|
|
|
|
**Auth:** `BODEGA`, `ADMIN`
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"mapping": {
|
|
"part_number": "NUMERO_PARTE",
|
|
"price": "PRECIO",
|
|
"stock": "EXISTENCIA",
|
|
"location": "ALMACEN"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### PUT /api/inventory/mapping
|
|
|
|
Guarda o actualiza el mapeo de columnas CSV/Excel.
|
|
|
|
**Auth:** `BODEGA`, `ADMIN`
|
|
|
|
**Request body:**
|
|
```json
|
|
{
|
|
"mapping": {
|
|
"part_number": "NUMERO_PARTE",
|
|
"price": "PRECIO",
|
|
"stock": "EXISTENCIA",
|
|
"location": "ALMACEN"
|
|
}
|
|
}
|
|
```
|
|
|
|
Los campos `part_number`, `price` y `stock` son requeridos en el mapeo. `location` es opcional (default: "Principal").
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"message": "Mapping saved",
|
|
"mapping": { "part_number": "NUMERO_PARTE", "price": "PRECIO", "stock": "EXISTENCIA" }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### POST /api/inventory/upload
|
|
|
|
Sube un archivo CSV o Excel (.xlsx) de inventario. Aplica el mapeo de columnas y hace UPSERT en `warehouse_inventory`.
|
|
|
|
**Auth:** `BODEGA`, `ADMIN`
|
|
|
|
**Request:** `multipart/form-data` con campo `file`
|
|
|
|
Proceso:
|
|
1. Lee el mapeo de columnas del usuario
|
|
2. Parsea el archivo (CSV o Excel)
|
|
3. Para cada fila, busca la parte por OEM part number (o aftermarket part number)
|
|
4. UPSERT en `warehouse_inventory` (user_id, part_id, warehouse_location)
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"message": "Upload processed",
|
|
"upload_id": 5,
|
|
"imported": 450,
|
|
"errors": 12,
|
|
"error_samples": [
|
|
{ "row": 15, "error": "Part not found: XYZ-999" }
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/inventory/uploads
|
|
|
|
Historial de uploads del usuario (ultimos 50).
|
|
|
|
**Auth:** `BODEGA`, `ADMIN`
|
|
|
|
**Response 200:**
|
|
```json
|
|
[
|
|
{
|
|
"id": 5,
|
|
"filename": "inventario_marzo.xlsx",
|
|
"status": "completed",
|
|
"rows_total": 462,
|
|
"rows_imported": 450,
|
|
"rows_errors": 12,
|
|
"created_at": "2026-03-18T10:00:00",
|
|
"completed_at": "2026-03-18T10:00:15"
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
### GET /api/inventory/items
|
|
|
|
Lista paginada del inventario del usuario actual.
|
|
|
|
**Auth:** `BODEGA`, `ADMIN`
|
|
|
|
**Query params:**
|
|
|
|
| Param | Tipo | Default | Descripcion |
|
|
|-------|------|---------|-------------|
|
|
| page | int | 1 | Pagina |
|
|
| per_page | int | 50 | Items por pagina (max 200) |
|
|
| q | string | - | Buscar por OEM number o nombre |
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"id": 100,
|
|
"part_id": 500,
|
|
"oem_part_number": "04152-YZZA1",
|
|
"name": "Oil Filter",
|
|
"price": 125.50,
|
|
"stock": 15,
|
|
"location": "Principal",
|
|
"updated_at": "2026-03-18T10:00:00"
|
|
}
|
|
],
|
|
"pagination": { "page": 1, "per_page": 50, "total": 450, "total_pages": 9 }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### DELETE /api/inventory/items
|
|
|
|
Elimina todo el inventario del usuario actual.
|
|
|
|
**Auth:** `BODEGA`, `ADMIN`
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"message": "Inventory cleared",
|
|
"deleted": 450
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Admin CRUD
|
|
|
|
Todos los endpoints CRUD de admin no requieren autenticacion actualmente (a implementar).
|
|
|
|
### Categories
|
|
|
|
| Metodo | Endpoint | Descripcion |
|
|
|--------|----------|-------------|
|
|
| POST | `/api/admin/categories` | Crear categoria |
|
|
| PUT | `/api/admin/categories/{id}` | Actualizar categoria |
|
|
| DELETE | `/api/admin/categories/{id}` | Eliminar categoria |
|
|
|
|
### Groups
|
|
|
|
| Metodo | Endpoint | Descripcion |
|
|
|--------|----------|-------------|
|
|
| GET | `/api/admin/groups?category_id=X` | Listar grupos |
|
|
| POST | `/api/admin/groups` | Crear grupo |
|
|
| PUT | `/api/admin/groups/{id}` | Actualizar grupo |
|
|
| DELETE | `/api/admin/groups/{id}` | Eliminar grupo |
|
|
|
|
### Parts
|
|
|
|
| Metodo | Endpoint | Descripcion |
|
|
|--------|----------|-------------|
|
|
| POST | `/api/admin/parts` | Crear parte |
|
|
| PUT | `/api/admin/parts/{id}` | Actualizar parte |
|
|
| DELETE | `/api/admin/parts/{id}` | Eliminar parte |
|
|
|
|
### Manufacturers
|
|
|
|
| Metodo | Endpoint | Descripcion |
|
|
|--------|----------|-------------|
|
|
| POST | `/api/admin/manufacturers` | Crear fabricante |
|
|
| PUT | `/api/admin/manufacturers/{id}` | Actualizar fabricante |
|
|
| DELETE | `/api/admin/manufacturers/{id}` | Eliminar fabricante |
|
|
|
|
### Aftermarket Parts
|
|
|
|
| Metodo | Endpoint | Descripcion |
|
|
|--------|----------|-------------|
|
|
| POST | `/api/admin/aftermarket` | Crear parte aftermarket |
|
|
| PUT | `/api/admin/aftermarket/{id}` | Actualizar parte aftermarket |
|
|
| DELETE | `/api/admin/aftermarket/{id}` | Eliminar parte aftermarket |
|
|
|
|
### Cross-References
|
|
|
|
| Metodo | Endpoint | Descripcion |
|
|
|--------|----------|-------------|
|
|
| GET | `/api/admin/crossref?page=1&per_page=50` | Listar cross-refs (paginado) |
|
|
| POST | `/api/admin/crossref` | Crear cross-reference |
|
|
| PUT | `/api/admin/crossref/{id}` | Actualizar cross-reference |
|
|
| DELETE | `/api/admin/crossref/{id}` | Eliminar cross-reference |
|
|
|
|
### Fitment (Vehicle-Part Links)
|
|
|
|
| Metodo | Endpoint | Descripcion |
|
|
|--------|----------|-------------|
|
|
| GET | `/api/admin/fitment?brand=X&model=Y&mye_id=Z` | Listar fitment (paginado) |
|
|
| POST | `/api/admin/fitment` | Crear fitment |
|
|
| DELETE | `/api/admin/fitment/{id}` | Eliminar fitment |
|
|
|
|
### CSV Import
|
|
|
|
`POST /api/admin/import/{type}`
|
|
|
|
Tipos validos: `categories`, `groups`, `parts`, `manufacturers`, `aftermarket`, `crossref`, `fitment`
|
|
|
|
**Request body:**
|
|
```json
|
|
{
|
|
"records": [
|
|
{ "name": "Engine", "name_es": "Motor", "slug": "engine" }
|
|
]
|
|
}
|
|
```
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"imported": 10,
|
|
"errors": ["Row 3: duplicate key..."]
|
|
}
|
|
```
|
|
|
|
### CSV Export
|
|
|
|
`GET /api/admin/export/{type}?page=1&per_page=1000`
|
|
|
|
Tipos validos: `categories`, `groups`, `parts`, `manufacturers`, `aftermarket`, `crossref`, `fitment`
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"data": [ { "id": 1, "name": "Engine", "name_es": "Motor" } ],
|
|
"pagination": { "page": 1, "per_page": 1000, "total": 10, "total_pages": 1 }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 11. Image Upload
|
|
|
|
### POST /api/admin/upload-image
|
|
|
|
Sube una imagen en base64 para partes.
|
|
|
|
**Auth:** No requerida (a proteger)
|
|
|
|
**Request body:**
|
|
```json
|
|
{
|
|
"image": "data:image/png;base64,iVBORw0KGgo..."
|
|
}
|
|
```
|
|
|
|
**Response 200:**
|
|
```json
|
|
{
|
|
"url": "/static/parts_images/abc123.png",
|
|
"filename": "abc123.png"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Formato de Paginacion
|
|
|
|
Todos los endpoints paginados retornan:
|
|
|
|
```json
|
|
{
|
|
"data": [],
|
|
"pagination": {
|
|
"page": 1,
|
|
"per_page": 50,
|
|
"total": 1000,
|
|
"total_pages": 20
|
|
}
|
|
}
|
|
```
|
|
|
|
## Codigos de Error
|
|
|
|
| Codigo | Descripcion |
|
|
|--------|-------------|
|
|
| 400 | Bad request (datos faltantes o invalidos) |
|
|
| 401 | No autenticado (token faltante, invalido o expirado) |
|
|
| 403 | Sin permisos (rol insuficiente o cuenta inactiva) |
|
|
| 404 | Recurso no encontrado |
|
|
| 409 | Conflicto (e.g., email duplicado) |
|
|
| 500 | Error interno del servidor |
|
|
| 502 | Error en API externa (NHTSA) |
|
|
| 503 | API externa no disponible |
|