ASP .NET MVC 5 - Gere relatórios em PDF com o plugin Rotativa(EF e pagedList) - I


  Neste artigo vou mostrar como podemos gerar relatórios no formato PDF usando o plugin Rotativa em aplicações ASP .NET MVC 5.

O objetivo deste artigo é mostrar como gerar relatórios no formato PDF e para isso vamos usar a biblioteca Rotativa que converte arquivos HTML para PDF usando a ferramenta wkhtmltopdf.exe para criar os arquivos PDF.

Essa ferramenta foi criada pelo italiano Giorgio Bozio, e a história você pode conferir neste link: http://letsfollowtheyellowbrickroad.blogspot.com.br/.

Para fazer esse trabalho podemos usar na linha de comando as ferramentas : wkhtmltopdf  e wkhtmltoimage  que são open-source. (veja o link: https://wkhtmltopdf.org/ )

Para usar na ASP .NET MVC 5 vamos instalar o pacote Rotativa em nosso projeto via Nuget. Vamos instalar também a biblioteca PagedList para realizar a paginação.

A paginação em aplicações ASP .NET MVC já foi tratada neste artigo : ASP .NET MVC - Realizando a paginação de dados - Macoratti.net

Além disso vamos usar o Entity Framework 6.x na abordagem Database-First para acessar um banco de dados SQL Server existente chamado VendasDB.mdf cuja estrutura é mostrada abaixo:

Vamos a seguir gerar um relatório de clientes, de pedidos e de vendas por período mostrando os pedidos por cliente.

Para gerar o PDF vamos usar a classe ViewAsPdf() informando o nome da view e o nome do modelo que usaremos na view.

                var relatorioPDF = new ViewAsPdf
                {
                    ViewName = "RelatorioClientes",
                    Model = listaClientes.ToPagedList(pagNumero, listaClientes.Count)
                };
                return relatorioPDF;

Esta classe pode usar a propriedade CustomSwitches que pode receber comandos para formatar o cabeçalho e o rodapé.

                var relatorioPDF = new ViewAsPdf
                {
                    string comandos = string.Format

                    ViewName = "RelatorioClientes",                   
                    CustomSwitches = "--footer-center \"Nome: " + "XYZ" + "  DOS: " +
                    DateTime.Now.Date.ToString("MM/dd/yyyy") + "  Pag.: [page]/[toPage]\"" +
                    " --footer-line --footer-font-size \"9\" --footer-spacing 6 --footer-font-name \"calibri light\""
                    IsGrayScale = true,
                    Model = listaClientes.ToPagedList(pagNumero, listaClientes.Count)
                };
                return relatorioPDF;

A seguir os principais comandos:

Isto posto, vamos ao trabalho...

Recursos Usados

Criando o projeto no VS 2017

Abra o Visual Studio 2017 Community crie um novo projeto ASP .NET Web Application usando a linguagem C# e Informe o nome Mvc5_RelatoriosPDF;

Use o template MVC, sem autenticação e clique em OK;

Instalando o Entity Framework 6.x

Com o projeto criado agora temos que referenciar o Entity Framework 6.x e definir no projeto pois vamos usá-lo para acessar os dados no SQL Server.

No menu Tools acione o Manage Nuget Package for Solutions, clique em Browse e localize o pacote EntityFramework, marque o projeto e clique em Install:

Instalando as bibliotecas Rotativa e PagedList

Agora vamos instalar as bibliotecas Rotativa para gerar o PDF..

Vamos usar desta vez o Package Manager Console para variar um pouco.

1- Instalando a biblioteca Rotativa

No menu Tools abra a janela do Packabe Manager Console e a seguir digite o comando: install-package Rotativa

Ao final será criada no projeto uma pasta chamada Rotativa contendo os arquivos wkhtmltopdf.exe e wkimagetopdf.exe:

1- Instalando a biblioteca PagedList

No menu Tools abra a janela do Packabe Manager Console e a seguir digite o comando: install-package PagedList.Mvc

Pronto, todas as referências estarão definidas na pasta References do projeto e agora já temos todos os recursos necessários para criar nossa aplicação ASP .NET MVC 5 e gerar relatórios no formato PDF.

Definindo o modelo de domínio

Em nosso modelo de domínio vamos criar as classes que iremos usar para gerar o relatório.

Assim vamos criar as classes : Cliente e Pedido.

Iremos criar todas as classes na pasta Models do projeto via menu Project-> Add Class.

1- Cliente

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace Mvc5_RelatoriosPDF.Models
{
    [Table("Clientes")]
    public class Cliente
    {
        public int ClienteId { get; set; }
        public string Nome { get; set; }
        public string Endereco { get; set; }
        public string Email { get; set; }
        public string Telefone { get; set; }
        public virtual ICollection<Pedido> Pedidos { get; set; }
        public Cliente()
        {
            Pedidos = new List<Pedido>();
        }
    }
}

2- Pedido

using System;
using System.ComponentModel.DataAnnotations.Schema;
namespace Mvc5_RelatoriosPDF.Models
{
    [Table("Pedidos")]
    public class Pedido
    {
        public int PedidoId { get; set; }
        public int ClienteId { get; set; }
        public DateTime PedidoData { get; set; }
        public decimal PedidoTotal { get; set; }
    }
}

Definindo a view model VendasViewModel

Agora vamos criar uma pasta chamada ViewModels em nosso projeto e criar nesta pasta uma classe chamada VendasViewModel onde vamos definir uma view model para vendas.

No menu Project clique em New Folder e informe o nome ViewModels.

A seguir crie a classe VendasViewModel nesta pasta com o seguinte código:

using System;
namespace Mvc5_RelatoriosPDF.ViewModels
{
    public class VendasViewModel
    {
        public string ClienteNome { get; set; }
        public string ClienteEmail { get; set; }
        public DateTime PedidoData { get; set; }
        public decimal PedidoTotal { get; set; }
    }
}

Uma ViewModel representa apenas os dados que queremos exibir na view. Assim, se quisermos exibir mais de um modelo na view, então precisamos criar um novo modelo para a view que no exemplo é a classe VendasViewModel.

Definindo a string de conexão no arquivo web.config

Precisamos definir a string de conexão no arquivo web.config para informar ao EF onde esta no banco de dados.

Abra o arquivo Web.Config do projeto e inclua o código abaixo:

...
<connectionStrings>
   <add name="VendasDbContext" connectionString="Data Source=.\;Initial Catalog=VendasDB;Integrated Security=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
...

Observe que o nome da string de conexão é o mesmo que o nome da nossa classe de Contexto: VendasDbContext.

Criando o controlador RelatoriosController e o relatório de clientes

Vamos criar um controlador RelatoriosController na pasta Controllers e inicialmente vamos gerar o relatório para clientes e depois para pedidos.

Clique com o botão direito sobre a pasta Controllers e a seguir clique em Add Controller;

Selecione o template MVC 5 Controller - Empty e informe o nome RelatoriosController.

A seguir vamos substituir o método Action Index pelo método RelatorioClientes() conforme mostra o código a seguir:

using Mvc5_RelatoriosPDF.Models;
using PagedList;
using Rotativa;
using System;
using System.Linq;
using System.Web.Mvc;
namespace Mvc5_RelatoriosPDF.Controllers
{
    public class RelatoriosController : Controller
    {
        private VendasDbContext db = new VendasDbContext();
        public ActionResult RelatorioClientes(int? pagina, Boolean? pdf)
        {
            var listaClientes = db.Clientes.OrderBy(c =>c.ClienteId ).ToList();
            if (pdf != true)
            {
                int numeroRegistros = 3;
                int numeroPagina = (pagina ?? 1);
                return View(listaClientes.ToPagedList(numeroPagina, numeroRegistros));
            }
            else
            {
                int pagNumero = 1;
                
                var relatorioPDF = new ViewAsPdf
                {
                    ViewName = "RelatorioClientes",
                    IsGrayScale = true,
                    Model = listaClientes.ToPagedList(pagNumero, listaClientes.Count)
                };
                return relatorioPDF;
            }
        }      
    }
}

Neste código criamos uma instância do nosso contexto VendasDbContext para usar no acesso e persistência das informações.

A view recebe dois parâmetros : pagina e pdf que indicam o número da página e se vamos gerar o relatório no formato PDF.

Se pdf for false ou null então passsamos uma relação de clientes obtidas e ordenadas pelo Id do cliente usando o PagedList().

Dessa forma teremos uma view tipada usando PagedList que via realizar a paginação.

Caso contrário usamos a classe ViewAsPdf para gerar o PDF a partir da view. No codigo estamos informando :

Agora vamos criar a View RelatorioClientes para exibir a lista de clientes. Nesta view vamos definir um link para poder o gerar o PDF.

Clique com o botão direito do mouse sobre o método Action RelatorioClientes e a seguir clique em Add View;

A seguir aceite o nome padrão e escolha o template List e Model Cliente conforme mostra a figura abaixo.   

Clique no botão Add e a seguir altere o código da view gerada conforme abaixo:

@model PagedList.IPagedList<Mvc5_RelatoriosPDF.Models.Cliente>
<h2>Relação de Clientes</h2>
<table class="table">
    <tr>
        <th>Nome</th>
        <th>Endereço</th>
        <th>Email</th>
        <th>Telefone</th>
        <th></th>
    </tr>
    @foreach (var item in Model)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Nome)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Endereco)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Email)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Telefone)
            </td>
        </tr>
    }
    <tr>
        <td><b>@Model.Count registros de @Model.TotalItemCount</b></td>
        <td><a href="/Relatorios/RelatorioClientes?pdf=true"><b>Gerar Relatório em PDF</b></a></td>
    </tr>
</table>
<!--páginação de dados -->
@{
  if (Model.TotalItemCount != Model.Count)
  { 
    <div class="row">
       <div class="col-md-12">
          Página @(Model.PageCount<Model.PageNumber? 0 : Model.PageNumber) de @Model.PageCount
            @if (Model.HasPreviousPage)
            {
               @Html.ActionLink("<<", "RelatorioClientes", new { pagina = 1, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter })
               @Html.Raw(" ");
               @Html.ActionLink("< Anterior", "RelatorioClientes", new { pagina = Model.PageNumber - 1, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter})
            }
            else
            {
               @:<<
               @Html.Raw(" ");
               @:< Anterior
            }
            @if(Model.HasNextPage)
            {
               @Html.ActionLink("Próxima >", "RelatorioClientes", new { pagina = Model.PageNumber + 1, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter })
               @Html.Raw(" ");
               @Html.ActionLink(">>", "RelatorioClientes", new { pagina = Model.PageCount, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter })
            }
            else
            {
               @:Próxima >
               @Html.Raw(" ")
               @:>>
            }
        </div>
    </div>
  }
}

Nesta view além da página que já expliquei em detalhes em outro artigo, temos um link que aciona a view para gerar o PDF : /Relatorios/RelatorioClientes?pdf=true"

Criando o relatório de pedidos

O relatório de pedidos é igualzinho ao de clientes por isso basta repetir todo o procedimento acima para pedidos.

Então primeiro crie um método Action RelatorioPedidos e a seguir a view RelatorioPedidos.

Abaixo temos o código do método Action RelatorioPedidos

       public ActionResult RelatorioPedidos(int? pagina, Boolean? pdf)
        {
            var listaPedidos = db.Pedidos.OrderBy(c => c.PedidoId).ToList();
            if (pdf != true)
            {
                int numeroRegistros = 5;
                int numeroPagina = (pagina ?? 1);
                return View(listaPedidos.ToPagedList(numeroPagina, numeroRegistros));
            }
            else
            {
                int pagNumero = 1;
                var relatorioPDF = new ViewAsPdf
                {
                    ViewName = "RelatorioPedidos",
                    IsGrayScale = true,
                    Model = listaPedidos.ToPagedList(pagNumero, listaPedidos.Count)
                };
                return relatorioPDF;
            }
        }

O código da view RelatorioPedidos segue abaixo:

@model PagedList.IPagedList<Mvc5_RelatoriosPDF.Models.Pedido>
@{
    ViewBag.Title = "RelatorioPedidos";
}
<h2>Relação de Pedidos</h2>
<table class="table">
    <tr>
        <th>
            Id Cliente
        </th>
        <th>
            Data Pedido
        </th>
        <th>
            Total do Pedido
        </th>
        <th></th>
    </tr>
    @foreach (var item in Model)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.ClienteId)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.PedidoData)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.PedidoTotal)
            </td>
        </tr>
    }
    <tr>
        <td><b>@Model.Count registros de @Model.TotalItemCount</b></td>
        <td><a href="/Relatorios/RelatorioPedidos?pdf=true"><b>Gerar Relatório em PDF</b></a></td>
    </tr>
</table>
<!--paginação de dados -->
@{
    if (Model.TotalItemCount != Model.Count)
    {
        <div class="row">
            <div class="col-md-12">
                Página @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) de @Model.PageCount
                @if (Model.HasPreviousPage)
                {
                    @Html.ActionLink("<<", "RelatorioPedidos", new { pagina = 1, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter })
                    @Html.Raw(" ");
                    @Html.ActionLink("< Anterior", "RelatorioPedidos", new { pagina = Model.PageNumber - 1, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter })
                }
                else
                {
                    @:<<
                    @Html.Raw(" ");
                    @:< Anterior
                }
                @if (Model.HasNextPage)
                {
                    @Html.ActionLink("Próxima >", "RelatorioPedidos", new { pagina = Model.PageNumber + 1, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter })
                    @Html.Raw(" ");
                    @Html.ActionLink(">>", "RelatorioPedidos", new { pagina = Model.PageCount, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter })
                }
                else
                {
                    @:Próxima >
                    @Html.Raw(" ")
                    @:>>
                }
            </div>
        </div>
    }
}

Executando o projeto iremos obter o seguinte resultado:

1- A relação de clientes com paginação e o link para gerar PDF

2- O relatório PDF dos clientes

Se desejar salvar o relatório basta clicar no botão download.

Para gerar o relatório como um arquivo diretamente basta definir a propriedade FileName da classe ViewAsPdf no controlador, atribuindo um nome para o relatório gerado :

               var relatorioPDF = new ViewAsPdf
                {
                    ViewName = "RelatorioClientes",
                    IsGrayScale = false,
                    FileName = "RelatorioClientesPDF",
                    Model = listaClientes.ToPagedList(pagNumero, listaClientes.Count)
                };
                return relatorioPDF;

Na próxima parte do artigo veremos como gerar o relatório de vendas por período que um pouco mais complexo.

Pegue o projeto completo aqui:  Mvc5_RelatoriosPDF.zip (sem as referências e os arquivos da pasta rotativa)

"Naquele tempo, respondendo Jesus, disse: Graças te dou, ó Pai, Senhor do céu e da terra, que ocultaste estas coisas aos sábios e entendidos, e as revelaste aos pequeninos."
Mateus 11:25

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Quer aprender a criar aplicações Web Dinâmicas usando a ASP .NET MVC 5 ?

  Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti