# 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 |
