Skip to main content

Reportes de Inventario

El módulo de reportes de inventario permite monitorear el stock, movimientos, valorización y productos críticos.

Tipos de Reportes

Reporte de Stock

Estado actual del inventario por almacén

Kardex de Productos

Historial completo de movimientos por producto

Alertas de Stock

Productos con stock bajo o agotado

Valorización

Valor total del inventario al costo

Reporte de Stock

1

Acceder al módulo

Navega a Almacén → Productos y selecciona un almacén.
2

Filtrar productos

Usa la búsqueda por código, nombre o código de barras.
3

Exportar

Haz clic en Descargar Excel para generar el reporte.

Exportación a Excel

Endpoint: POST /api/productos/export/excel Parámetros:
{
  almacen: '1',        // ID del almacén (1, 2, 3)
  texto: 'laptop'      // Búsqueda opcional
}
Archivo generado: productos-almacen-{almacen}-YYYY-MM-DD-HHMMSS.xlsx

Columnas del Reporte

ColumnaDescripciónFormato
CódigoCódigo internoPROD-001
NombreNombre del productoLaptop HP 15”
DescripciónDetalle completoEspecificaciones técnicas
CategoríaCategoría asignadaElectrónica
UnidadUnidad de medidaUNIDAD, CAJA, METRO
StockCantidad en almacén45
CostoPrecio de costo1,500.00
Precio VentaPrecio al público2,200.00
Precio DistribuidorPrecio mayorista1,900.00
Precio MayoristaPrecio al por mayor2,000.00
Características:
  • Encabezado con título naranja (#F97316)
  • Fecha de generación y filtro aplicado
  • Filas alternadas (gris claro #F9FAFB)
  • Fila de TOTAL al final con suma de stock
  • Formato numérico para precios: #,##0.00
// ProductoExportController.php:130-275
public function descargarExcel(Request $request)
{
    $user = $request->user();
    $almacen = $request->get('almacen', '1');
    $busqueda = $request->get('texto', '');
    
    // Usar vista de base de datos específica del almacén
    $query = DB::table("view_productos_$almacen")
        ->where('id_empresa', $user->id_empresa);
    
    // Aplicar búsqueda
    if (!empty($busqueda)) {
        $query->where(function($q) use ($busqueda) {
            $q->where('codigo', 'like', "%$busqueda%")
              ->orWhere('nombre', 'like', "%$busqueda%")
              ->orWhere('descripcion', 'like', "%$busqueda%")
              ->orWhere('cod_barra', 'like', "%$busqueda%");
        });
    }
    
    $productos = $query->orderBy('codigo')->get();
    
    // Total de stock
    $totalRow = $row + 1;
    $sheet->setCellValue('E' . $totalRow, 'TOTAL:');
    $sheet->setCellValue('F' . $totalRow, "=SUM(F{$headerRow + 1}:F{$row - 1})");
}

Kardex de Movimientos

El kardex registra todos los movimientos de entrada y salida de cada producto.

Modelo MovimientoStock

Tabla: movimientos_stock Columnas principales:
  • id_producto: Producto afectado
  • tipo_movimiento: entrada, salida, ajuste, transferencia
  • cantidad: Unidades movidas (+ o -)
  • stock_anterior: Stock antes del movimiento
  • stock_actual: Stock después del movimiento
  • motivo: Razón del movimiento
  • documento_referencia: Venta, compra o guía asociada
  • id_almacen: Almacén afectado
  • id_usuario: Usuario responsable
  • created_at: Fecha y hora del movimiento

Tipos de Movimiento

Aumentan el inventario
  • Compra: Al registrar una compra, se crea un movimiento de entrada por cada producto
  • Ajuste positivo: Corrección manual de inventario (sobrantes)
  • Transferencia entrante: Recepción desde otro almacén
  • Devolución: Cliente devuelve un producto
MovimientoStock::create([
    'id_producto' => $producto->id,
    'tipo_movimiento' => 'entrada',
    'cantidad' => 50,
    'stock_anterior' => 100,
    'stock_actual' => 150,
    'motivo' => 'Compra a proveedor XYZ',
    'documento_referencia' => 'F001-00000123'
]);
Reducen el inventario
  • Venta: Al emitir una factura/boleta, se descuenta el stock
  • Ajuste negativo: Corrección manual (faltantes, mermas)
  • Transferencia saliente: Envío a otro almacén
  • Baja: Producto dañado o vencido
MovimientoStock::create([
    'id_producto' => $producto->id,
    'tipo_movimiento' => 'salida',
    'cantidad' => -10,
    'stock_anterior' => 150,
    'stock_actual' => 140,
    'motivo' => 'Venta B001-00000456',
    'documento_referencia' => 'B001-00000456'
]);

Consultar Kardex

// Obtener movimientos de un producto
const kardex = await fetch(
  `/api/productos/${productoId}/movimientos?desde=2024-01-01&hasta=2024-12-31`
);

const movimientos = await kardex.json();

// Renderizar tabla
movimientos.forEach(mov => {
  console.log(`
    ${mov.created_at}: ${mov.tipo_movimiento}
    Cantidad: ${mov.cantidad}
    Stock: ${mov.stock_anterior}${mov.stock_actual}
    Motivo: ${mov.motivo}
  `);
});

Alertas de Stock Bajo

Identifica productos que necesitan reabastecimiento.

Configurar Stock Mínimo

  1. Ve a Almacén → Productos
  2. Edita un producto
  3. Configura el campo Stock Mínimo
// En el modelo Producto
public function scopeStockBajo($query, $almacenId = 1)
{
    return $query->whereRaw(
        "cantidad_{$almacenId} <= stock_minimo AND cantidad_{$almacenId} > 0"
    );
}

public function scopeStockAgotado($query, $almacenId = 1)
{
    return $query->where("cantidad_{$almacenId}", 0);
}

Generar Reporte de Alertas

const getAlertasStock = async (almacen = 1) => {
  const response = await fetch(
    `/api/productos/alertas?almacen=${almacen}`
  );
  
  const { stockBajo, stockAgotado } = await response.json();
  
  return {
    criticos: stockAgotado,       // Stock = 0
    atencion: stockBajo,          // Stock <= mínimo
    total: stockBajo.length + stockAgotado.length
  };
};
Criterios de alerta:
  • 🔴 Crítico: stock = 0 (producto agotado)
  • 🟡 Atención: stock <= stock_minimo (requiere reabastecimiento)
  • 🟢 Normal: stock > stock_minimo
Configura notificaciones automáticas para recibir alertas diarias de productos con stock bajo.

Valorización de Inventario

Calcula el valor total del inventario al precio de costo.

Fórmula

Valor Total = Σ (Stock × Costo Unitario)

Ejemplo de Cálculo

ProductoStockCostoValor
Laptop HP15S/ 1,800S/ 27,000
Mouse Logitech50S/ 35S/ 1,750
Teclado Mecánico20S/ 120S/ 2,400
TOTALS/ 31,150

Generar Reporte de Valorización

const calcularValorizacion = (productos) => {
  const resumen = {
    valorTotal: 0,
    totalProductos: productos.length,
    totalUnidades: 0,
    porCategoria: {}
  };
  
  productos.forEach(p => {
    const valor = p.cantidad * p.costo;
    resumen.valorTotal += valor;
    resumen.totalUnidades += p.cantidad;
    
    // Agrupar por categoría
    const cat = p.categoria || 'Sin categoría';
    if (!resumen.porCategoria[cat]) {
      resumen.porCategoria[cat] = {
        nombre: cat,
        valor: 0,
        productos: 0
      };
    }
    resumen.porCategoria[cat].valor += valor;
    resumen.porCategoria[cat].productos++;
  });
  
  return resumen;
};

Plantilla de Importación

Para cargar productos masivamente, descarga la plantilla Excel. Endpoint: GET /api/productos/plantilla Columnas de la plantilla:
  1. Código
  2. Producto
  3. Detalle
  4. Categoría
  5. Unidad
  6. Moneda (PEN/USD)
  7. Costo
  8. Stock
  9. Precio Venta
  10. Precio Distribuidor
  11. Precio Mayorista
Fila de ejemplo incluida:
PROD-001 | Nombre del producto | Descripción opcional | Repuestos | 
UNIDAD | PEN | 10.50 | 100 | 15.00 | 13.00 | 12.00
// ProductoExportController.php:19-125
public function descargarPlantilla(Request $request)
{
    $spreadsheet = new Spreadsheet();
    $sheet = $spreadsheet->getActiveSheet();
    
    // Encabezados
    $headers = [
        'A1' => 'Código',
        'B1' => 'Producto',
        'C1' => 'Detalle',
        // ... más columnas
    ];
    
    // Fila de ejemplo con fondo amarillo
    $sheet->setCellValue('A2', 'PROD-001');
    $sheet->getStyle('A2:K2')->applyFromArray([
        'fill' => ['fillType' => Fill::FILL_SOLID, 
                   'startColor' => ['rgb' => 'FFF9C4']]
    ]);
    
    // Comentario explicativo
    $sheet->getComment('A2')->getText()
        ->createTextRun('Fila de ejemplo — eliminar antes de importar');
}
Elimina la fila de ejemplo antes de importar. Los códigos de producto deben ser únicos.

Transferencias entre Almacenes

Para mover productos entre almacenes:
  1. El sistema tiene 3 almacenes configurados (usa vistas view_productos_1, view_productos_2, view_productos_3)
  2. Cada transferencia genera 2 movimientos:
    • Salida del almacén origen
    • Entrada al almacén destino
// Transferir producto
$producto->transferir(
    $almacenOrigen = 1,
    $almacenDestino = 2,
    $cantidad = 10,
    $motivo = 'Reubicación de inventario'
);

// Genera movimientos:
MovimientoStock::create([
    'tipo_movimiento' => 'transferencia',
    'cantidad' => -10,  // Salida
    'id_almacen' => 1
]);

MovimientoStock::create([
    'tipo_movimiento' => 'transferencia',
    'cantidad' => 10,   // Entrada
    'id_almacen' => 2
]);

Solución de Problemas

Causa: Movimientos no registrados o ajustes manuales en la base de datos.Solución:
  1. Consulta el kardex del producto para ver todos los movimientos
  2. Verifica que todas las ventas y compras estén registradas correctamente
  3. Si encuentras inconsistencias, haz un ajuste manual:
    • Ve a Almacén → Productos
    • Edita el producto
    • Actualiza el stock manualmente
    • Se creará un movimiento de tipo “ajuste”
Causa: Filtro de almacén incorrecto.Solución: Asegúrate de seleccionar el almacén correcto (1, 2 o 3). Cada almacén tiene su propia vista de productos.
Causa: El producto no tiene costo registrado.Solución:
  1. Ve a Almacén → Productos
  2. Edita cada producto y completa el campo Costo
  3. El costo debe actualizarse con cada compra automáticamente

Próximos Pasos

Gestión de Productos

Aprende a crear y editar productos

Compras

Registra compras para aumentar stock

Ventas

Las ventas reducen el stock automáticamente

Reportes Financieros

Analiza la rentabilidad de tu inventario

Build docs developers (and LLMs) love