Files
Autoparts-DB/docs/API.md
consultoria-as fb6ea31100 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>
2026-03-18 22:30:53 +00:00

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 |