# 📦 VentaBack — Documentación de API REST

> **Base URL:** `http://localhost:8000/api`  
> **Autenticación:** Bearer Token (Laravel Sanctum)  
> **Content-Type:** `application/json`

---

## 🔐 Autenticación

Todos los endpoints protegidos requieren el header:

```
Authorization: Bearer {{token}}
```

El token se obtiene en los endpoints de **login** y debe enviarse en cada petición autenticada.

---

## 📑 Tabla de Contenidos

- [👤 Clientes — Auth](#-clientes--auth)
- [🛍️ Catálogo de Productos (público)](#️-catálogo-de-productos-público)
- [🛒 Carrito de Compras](#-carrito-de-compras)
- [💳 Ventas / Checkout (Cliente)](#-ventas--checkout-cliente)
- [⭐ Puntuaciones y Reseñas](#-puntuaciones-y-reseñas)
- [💡 Recomendaciones](#-recomendaciones)
- [🔑 Usuarios Internos — Auth](#-usuarios-internos--auth)
- [📦 Gestión de Productos (Admin/Almacén)](#-gestión-de-productos-adminalmacén)
- [📊 Inventario y Stock](#-inventario-y-stock)
- [📋 Auditoría — Logs](#-auditoría--logs)

---

## 👤 Clientes — Auth

### `POST /v1/client/register`

Registra un nuevo cliente en el sistema (App Móvil / E-commerce).

| Campo            | Tipo    | Requerido | Descripción                          |
|-----------------|---------|-----------|--------------------------------------|
| `nombre`         | string  | ✅        | Nombre del cliente (máx. 50 chars)   |
| `apellido`       | string  | ✅        | Apellido (máx. 50 chars)             |
| `cedula_rif`     | string  | ✅        | Cédula o RIF único (máx. 20 chars)   |
| `correo`         | string  | ✅        | Email único y válido (máx. 50 chars) |
| `contrasena`     | string  | ✅        | Contraseña (mín. 6 chars)            |
| `direccion`      | string  | ✅        | Dirección principal del cliente      |
| `telefono`       | string  | ❌        | Teléfono (máx. 20 chars)             |
| `direccion_envio`| string  | ❌        | Dirección de envío alternativa       |

**Request Body:**
```json
{
    "nombre": "María",
    "apellido": "González",
    "cedula_rif": "V-12345678",
    "telefono": "+58 412 555 0000",
    "correo": "maria@example.com",
    "direccion": "Av. Principal, Caracas",
    "direccion_envio": "Calle 5, Local 3, Valencia",
    "contrasena": "miPassword123"
}
```

**Response `201 Created`:**
```json
{
    "message": "Cliente registrado exitosamente.",
    "access_token": "1|abcdef1234567890...",
    "token_type": "Bearer",
    "client": {
        "id": 1,
        "nombre": "María",
        "apellido": "González",
        "cedula_rif": "V-12345678",
        "correo": "maria@example.com",
        "direccion": "Av. Principal, Caracas"
    }
}
```

---

### `POST /v1/client/login`

Inicia sesión como cliente.

| Campo        | Tipo   | Requerido | Descripción       |
|-------------|--------|-----------|-------------------|
| `correo`     | string | ✅        | Email del cliente |
| `contrasena` | string | ✅        | Contraseña        |

**Request Body:**
```json
{
    "correo": "maria@example.com",
    "contrasena": "miPassword123"
}
```

**Response `200 OK`:**
```json
{
    "message": "Login exitoso.",
    "access_token": "1|abcdef1234567890...",
    "token_type": "Bearer",
    "client": {
        "id": 1,
        "nombre": "María",
        "apellido": "González",
        "correo": "maria@example.com"
    }
}
```

**Response `401 Unauthorized`:**
```json
{
    "message": "Credenciales inválidas."
}
```

---

### `POST /v1/client/logout` 🔒

Cierra la sesión del cliente autenticado e invalida el token actual.

**Headers:** `Authorization: Bearer {{client_token}}`

**Response `200 OK`:**
```json
{
    "message": "Sesión cerrada."
}
```

---

## 🛍️ Catálogo de Productos (público)

### `GET /v1/client/productos`

Lista todos los productos disponibles en el catálogo. Soporta filtros por categoría y búsqueda por nombre.

**Query Parameters:**

| Parámetro  | Tipo   | Requerido | Descripción                              |
|-----------|--------|-----------|------------------------------------------|
| `categoria`| string | ❌        | Filtra por categoría exacta              |
| `buscar`   | string | ❌        | Búsqueda por nombre (parcial, insensible)|

**Ejemplos de Request:**
```
GET /api/v1/client/productos
GET /api/v1/client/productos?categoria=Ropa
GET /api/v1/client/productos?buscar=camisa
GET /api/v1/client/productos?categoria=Ropa&buscar=camisa
```

**Response `200 OK`:**
```json
[
    {
        "id_producto": 1,
        "nombre": "Camisa Polo Slim Fit",
        "categoria": "Ropa",
        "descripcion": "Camisa polo de algodón premium...",
        "precio": "45.99",
        "descuento": "10.00",
        "stock_actual": 120,
        "stock_minimo": 10,
        "caracteristicas": { "material": "Algodón", "tallas": ["S", "M", "L", "XL"] },
        "imagenes": [
            {
                "id": 1,
                "url_imagen": "https://example.com/img/camisa.jpg",
                "es_principal": true
            }
        ],
        "tamanios": [],
        "puntuaciones": []
    }
]
```

---

### `GET /v1/client/productos/{id}`

Obtiene el detalle de un producto específico por su ID.

**Path Parameter:** `id` — ID del producto

**Response `200 OK`:**
```json
{
    "id_producto": 1,
    "nombre": "Camisa Polo Slim Fit",
    "categoria": "Ropa",
    "precio": "45.99",
    "descuento": "10.00",
    "stock_actual": 120,
    "imagenes": [],
    "tamanios": [],
    "puntuaciones": []
}
```

**Response `404 Not Found`:**
```json
{
    "message": "No query results for model [App\\Models\\Producto] 99"
}
```

---

## 🛒 Carrito de Compras

> ⚠️ **Requiere autenticación como cliente.**  
> Headers: `Authorization: Bearer {{client_token}}`

### `GET /v1/client/carrito` 🔒

Obtiene todos los productos en el carrito del cliente autenticado.

**Response `200 OK`:**
```json
[
    {
        "id": 5,
        "id_cliente": 1,
        "id_producto": 3,
        "id_tamanio": 2,
        "cantidad": 2,
        "producto": {
            "id_producto": 3,
            "nombre": "Zapatilla Urban Runner",
            "precio": "89.99",
            "imagenes": []
        },
        "tamanio": {
            "id": 2,
            "descripcion": "42"
        }
    }
]
```

---

### `POST /v1/client/carrito` 🔒

Agrega un producto al carrito. Si el producto ya existe (mismo producto y talla), se incrementa la cantidad.

| Campo        | Tipo    | Requerido | Descripción                            |
|-------------|---------|-----------|----------------------------------------|
| `id_producto`| integer | ✅        | ID del producto a agregar              |
| `cantidad`   | integer | ✅        | Cantidad (mín. 1)                      |
| `id_tamanio` | integer | ❌        | ID de la talla/tamaño (si aplica)      |

**Request Body:**
```json
{
    "id_producto": 3,
    "id_tamanio": 2,
    "cantidad": 2
}
```

**Response `201 Created`:**
```json
{
    "message": "Producto agregado al carrito.",
    "item": {
        "id": 5,
        "id_cliente": 1,
        "id_producto": 3,
        "id_tamanio": 2,
        "cantidad": 2
    }
}
```

---

### `DELETE /v1/client/carrito/{id}` 🔒

Elimina un ítem específico del carrito del cliente autenticado.

**Path Parameter:** `id` — ID del ítem en el carrito

**Response `200 OK`:**
```json
{
    "message": "Producto eliminado del carrito."
}
```

---

## 💳 Ventas / Checkout (Cliente)

> ⚠️ **Requiere autenticación como cliente.**  
> Headers: `Authorization: Bearer {{client_token}}`

### `POST /v1/client/ventas` 🔒

Procesa la compra a partir de los ítems actuales en el carrito del cliente. Aplica IVA del 16%, descuentos por producto y descuenta el stock automáticamente. Limpia el carrito al finalizar.

> 📌 No requiere body — usa el carrito activo del cliente autenticado.

**Response `201 Created`:**
```json
{
    "message": "Compra realizada con éxito.",
    "id_venta": 42,
    "total": 211.37
}
```

**Response `400 Bad Request` (carrito vacío):**
```json
{
    "message": "El carrito está vacío."
}
```

**Response `422 Unprocessable Entity` (stock insuficiente):**
```json
{
    "message": "Stock insuficiente para el producto: Zapatilla Urban Runner",
    "stock_disponible": 1
}
```

---

### `GET /v1/client/mis-ventas` 🔒

Devuelve el historial de compras del cliente autenticado, ordenado de más reciente a más antiguo.

**Response `200 OK`:**
```json
[
    {
        "id_venta": 42,
        "subtotal": "181.99",
        "monto_impuesto": "29.12",
        "monto_descuento": "0.00",
        "total": "211.11",
        "fecha_pago": "2026-08-17T23:00:00.000000Z",
        "estatus": {
            "id_estatus": 1,
            "descripcion": "PAGADA"
        },
        "detalle_ventas": [
            {
                "id_producto": 3,
                "cantidad": 2,
                "precio_unitario": "80.99",
                "subtotal": "161.98",
                "producto": {
                    "nombre": "Zapatilla Urban Runner"
                }
            }
        ]
    }
]
```

---

## ⭐ Puntuaciones y Reseñas

### `GET /v1/client/productos/{id}/resenas`

Endpoint **público**. Lista todas las reseñas de un producto con el promedio de estrellas.

**Path Parameter:** `id` — ID del producto

**Response `200 OK`:**
```json
{
    "promedio_estrellas": 4.3,
    "total_opiniones": 12,
    "resenas": [
        {
            "id": 8,
            "id_cliente": 2,
            "id_producto": 1,
            "estrellas": 5,
            "comentario": "Excelente calidad, muy cómodo."
        }
    ]
}
```

---

### `POST /v1/client/productos/{id}/calificar` 🔒

Permite al cliente autenticado calificar un producto. Si el cliente ya calificó el producto, **actualiza** la calificación existente.

**Path Parameter:** `id` — ID del producto a calificar

| Campo       | Tipo    | Requerido | Descripción                         |
|------------|---------|-----------|-------------------------------------|
| `estrellas` | integer | ✅        | Puntuación del 1 al 5               |
| `comentario`| string  | ❌        | Comentario o reseña (máx. 500 chars)|

**Request Body:**
```json
{
    "estrellas": 5,
    "comentario": "Excelente producto, llegó en perfectas condiciones."
}
```

**Response `201 Created`:**
```json
{
    "message": "Calificación registrada correctamente.",
    "puntuacion": {
        "id": 8,
        "id_cliente": 1,
        "id_producto": 3,
        "estrellas": 5,
        "comentario": "Excelente producto, llegó en perfectas condiciones."
    }
}
```

---

## 💡 Recomendaciones

### `GET /v1/client/recomendaciones` 🔒

Devuelve productos recomendados para el cliente autenticado. Si existen recomendaciones personalizadas asignadas, las devuelve; de lo contrario, retorna 5 productos aleatorios con stock disponible.

**Headers:** `Authorization: Bearer {{client_token}}`

**Response `200 OK` — Recomendaciones personalizadas:**
```json
{
    "tipo": "personalizadas",
    "recomendaciones": [
        {
            "id": 1,
            "id_cliente": 1,
            "id_producto": 5,
            "producto": {
                "id_producto": 5,
                "nombre": "Jean Slim Fit",
                "precio": "55.00",
                "imagenes": []
            }
        }
    ]
}
```

**Response `200 OK` — Sugerencias generales:**
```json
{
    "tipo": "sugerencias_generales",
    "productos": [
        {
            "id_producto": 7,
            "nombre": "Camiseta Básica",
            "precio": "20.00",
            "imagenes": []
        }
    ]
}
```

---

## 🔑 Usuarios Internos — Auth

### `POST /v1/user/login`

Inicia sesión como usuario interno (Administrador, Vendedor, Almacén). Registra el intento en el log de accesos.

| Campo       | Tipo   | Requerido | Descripción              |
|------------|--------|-----------|--------------------------|
| `nombre`    | string | ✅        | Nombre de usuario        |
| `contraseña`| string | ✅        | Contraseña del usuario   |

**Request Body:**
```json
{
    "nombre": "admin_user",
    "contraseña": "secretPassword"
}
```

**Response `200 OK`:**
```json
{
    "message": "Login exitoso.",
    "access_token": "2|xyz789abc...",
    "token_type": "Bearer",
    "user": {
        "id_usuario": 1,
        "nombre": "admin_user",
        "rol": "Administrador"
    }
}
```

**Response `401 Unauthorized`:**
```json
{
    "message": "Credenciales inválidas."
}
```

> 📝 Los intentos de acceso (exitosos y fallidos) quedan registrados automáticamente en el log de accesos con IP y timestamp.

---

### `POST /v1/user/logout` 🔒

Cierra la sesión del usuario interno y revoca el token actual.

**Headers:** `Authorization: Bearer {{user_token}}`

**Response `200 OK`:**
```json
{
    "message": "Sesión cerrada."
}
```

---

## 📦 Gestión de Productos (Admin/Almacén)

> ⚠️ **Requiere autenticación como usuario interno.**  
> 🛡️ **Roles permitidos:** `Administrador`, `Almacén`  
> Headers: `Authorization: Bearer {{user_token}}`

### `POST /v1/user/productos` 🔒🛡️

Crea un nuevo producto en el catálogo. Soporta múltiples imágenes (URLs). La primera imagen del array se marcará como principal.

| Campo            | Tipo    | Requerido | Descripción                              |
|-----------------|---------|-----------|------------------------------------------|
| `nombre`         | string  | ✅        | Nombre del producto (máx. 100 chars)     |
| `categoria`      | string  | ✅        | Categoría (máx. 50 chars)               |
| `precio`         | numeric | ✅        | Precio (mín. 0)                          |
| `stock_actual`   | integer | ✅        | Stock inicial disponible (mín. 0)        |
| `stock_minimo`   | integer | ✅        | Stock mínimo para alerta (mín. 1)        |
| `descripcion`    | string  | ❌        | Descripción del producto                 |
| `descuento`      | numeric | ❌        | Porcentaje de descuento (0–100)          |
| `caracteristicas`| object  | ❌        | Objeto JSON con atributos del producto   |
| `imagenes`       | array   | ❌        | Array de URLs de imágenes del producto   |

**Request Body:**
```json
{
    "nombre": "Chaqueta Impermeable",
    "categoria": "Ropa",
    "descripcion": "Chaqueta impermeable para exteriores, ligera y resistente.",
    "precio": 129.99,
    "descuento": 15,
    "caracteristicas": {
        "material": "Nylon",
        "tallas": ["S", "M", "L", "XL"],
        "colores": ["Negro", "Azul marino"]
    },
    "stock_actual": 80,
    "stock_minimo": 10,
    "imagenes": [
        "https://cdn.example.com/chaqueta-front.jpg",
        "https://cdn.example.com/chaqueta-back.jpg"
    ]
}
```

**Response `201 Created`:**
```json
{
    "message": "Producto creado exitosamente.",
    "producto": {
        "id_producto": 12,
        "nombre": "Chaqueta Impermeable",
        "categoria": "Ropa",
        "precio": "129.99",
        "descuento": "15.00",
        "stock_actual": 80,
        "stock_minimo": 10,
        "imagenes": [
            {
                "id": 20,
                "id_producto": 12,
                "url_imagen": "https://cdn.example.com/chaqueta-front.jpg",
                "es_principal": true
            },
            {
                "id": 21,
                "id_producto": 12,
                "url_imagen": "https://cdn.example.com/chaqueta-back.jpg",
                "es_principal": false
            }
        ]
    }
}
```

**Response `422 Unprocessable Entity` (validación fallida):**
```json
{
    "message": "The nombre field is required.",
    "errors": {
        "nombre": ["The nombre field is required."],
        "precio": ["The precio field must be at least 0."]
    }
}
```

---

### `PUT /v1/user/productos/{id}` 🔒🛡️

Actualiza los datos de un producto existente.

**Path Parameter:** `id` — ID del producto

> 📌 Mismo body y validaciones que `POST /v1/user/productos`.

---

## 📊 Inventario y Stock

> ⚠️ **Requiere autenticación como usuario interno.**  
> Headers: `Authorization: Bearer {{user_token}}`

### `GET /v1/user/inventario/alertas-stock` 🔒

Lista todos los productos cuyo `stock_actual` es menor o igual al `stock_minimo`, ordenados por stock más crítico primero.

**Response `200 OK`:**
```json
{
    "total_alertas": 3,
    "productos": [
        {
            "id_producto": 5,
            "nombre": "Camiseta Básica Blanca",
            "categoria": "Ropa",
            "stock_actual": 2,
            "stock_minimo": 10,
            "precio": "20.00"
        }
    ]
}
```

---

### `PATCH /v1/user/productos/{id}/stock` 🔒

Actualiza el stock de un producto. Acepta cantidades **positivas** (entrada de mercancía) o **negativas** (salida/ajuste manual). No permite stock resultante negativo.

**Path Parameter:** `id` — ID del producto

| Campo     | Tipo    | Requerido | Descripción                                                  |
|----------|---------|-----------|--------------------------------------------------------------|
| `cantidad`| integer | ✅        | Cantidad a sumar/restar (positivo=entrada, negativo=salida)  |
| `motivo`  | string  | ✅        | Justificación del ajuste (máx. 255 chars)                    |

**Request Body — Entrada de stock:**
```json
{
    "cantidad": 50,
    "motivo": "Recepción de nueva mercancía — Proveedor XYZ"
}
```

**Request Body — Salida/ajuste manual:**
```json
{
    "cantidad": -5,
    "motivo": "Ajuste por productos dañados en bodega"
}
```

**Response `200 OK`:**
```json
{
    "message": "Stock actualizado correctamente.",
    "producto": {
        "id_producto": 5,
        "nombre": "Camiseta Básica Blanca",
        "stock_actual": 52,
        "stock_minimo": 10
    },
    "bajo_stock": false
}
```

**Response `422 Unprocessable Entity` (stock resultante negativo):**
```json
{
    "message": "El stock resultante no puede ser negativo."
}
```

---

## 📋 Auditoría — Logs

> ⚠️ **Requiere autenticación como usuario interno.**  
> 🛡️ **Roles permitidos:** `Administrador`, `Almacén`  
> Headers: `Authorization: Bearer {{user_token}}`

### `GET /v1/user/logs/acciones` 🔒🛡️

Devuelve el historial de acciones operativas realizadas en el sistema (ventas registradas, productos creados, stock modificado, etc.). Paginado de 20 registros por página.

**Response `200 OK`:**
```json
{
    "current_page": 1,
    "data": [
        {
            "id_log": 45,
            "accion": "Venta registrada",
            "detalles": "Venta #42 por $211.11",
            "fecha_hora": "2026-08-17T23:05:00.000000Z",
            "usuario": {
                "id_usuario": 3,
                "nombre": "vendedor01",
                "rol": "Vendedor"
            }
        }
    ],
    "per_page": 20,
    "total": 45
}
```

---

### `GET /v1/user/logs/accesos` 🔒🛡️

Devuelve el historial de accesos al sistema (logins exitosos y fallidos) con IP registrada. Paginado de 20 registros por página.

**Response `200 OK`:**
```json
{
    "current_page": 1,
    "data": [
        {
            "id_log_acceso": 80,
            "fecha_hora": "2026-08-17T19:00:00.000000Z",
            "ip": "192.168.1.10",
            "estado": "EXITOSO",
            "usuario": {
                "id_usuario": 1,
                "nombre": "admin_user",
                "rol": "Administrador"
            }
        },
        {
            "id_log_acceso": 79,
            "fecha_hora": "2026-08-17T18:55:00.000000Z",
            "ip": "192.168.1.22",
            "estado": "FALLIDO",
            "usuario": null
        }
    ],
    "per_page": 20,
    "total": 80
}
```

---

## 🗺️ Resumen de Endpoints

### 👤 Módulo Cliente — `/api/v1/client/`

| Método   | Endpoint                        | Auth | Descripción                       |
|---------|---------------------------------|------|-----------------------------------|
| `POST`   | `/register`                     | ❌   | Registro de cliente               |
| `POST`   | `/login`                        | ❌   | Login de cliente                  |
| `POST`   | `/logout`                       | 🔒   | Logout de cliente                 |
| `GET`    | `/productos`                    | ❌   | Listar catálogo (con filtros)     |
| `GET`    | `/productos/{id}`               | ❌   | Detalle de un producto            |
| `GET`    | `/productos/{id}/resenas`       | ❌   | Reseñas y promedio de un producto |
| `POST`   | `/productos/{id}/calificar`     | 🔒   | Calificar un producto             |
| `GET`    | `/carrito`                      | 🔒   | Ver carrito                       |
| `POST`   | `/carrito`                      | 🔒   | Agregar ítem al carrito           |
| `DELETE` | `/carrito/{id}`                 | 🔒   | Eliminar ítem del carrito         |
| `POST`   | `/ventas`                       | 🔒   | Procesar compra (checkout)        |
| `GET`    | `/mis-ventas`                   | 🔒   | Historial de compras del cliente  |
| `GET`    | `/recomendaciones`              | 🔒   | Productos recomendados            |

### 🔑 Módulo Usuarios Internos — `/api/v1/user/`

| Método | Endpoint | Auth | Rol | Descripción |
| --- | --- | --- | --- | --- |
| `POST` | `/v1/user/login` | ❌ | — | Login de usuario interno

 |
| `POST` | `/v1/user/logout` | 🔒 | Cualquiera | Logout de usuario interno

 |
| `POST` | `/v1/user/productos` | 🔒🛡️ | Admin / Almacén | Crear producto

 |
| `PUT` | `/v1/user/productos/{id}` | 🔒🛡️ | Admin / Almacén | Actualizar producto

 |
| `PATCH` | `/v1/user/productos/{id}/stock` | 🔒🛡️ | Admin / Almacén | Ajuste de stock (entrada/salida)

 |
| `GET` | `/v1/user/inventario/alertas` | 🔒 | Cualquiera | Alertas de stock crítico |
| `GET` | `/v1/user/logs/acciones` | 🔒🛡️ | Admin / Almacén | Log de acciones del sistema

 |
| `GET` | `/v1/user/logs/accesos` | 🔒🛡️ | Admin / Almacén | Log de accesos (logins)

 |

---

> **Leyenda:**  
> 🔒 Requiere token Bearer (Sanctum)  
> 🛡️ Restringido por rol específico  
> ❌ Endpoint público, sin autenticación
