.NET

16 jul, 2018

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

Publicidade

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

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 seguintes 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. Instalaremos também a biblioteca PagedList para realizar a paginação.

A paginação em aplicações ASP .NET MVC já foi tratada no seguinte artigo:

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:

  • –header-center \”texto\”: escreve um texto no cabeçalho. No caso, na região central do mesmo. Center pode ser substituído por right ou left;
  • –header-spacing \”valor\”: determina o espaçamento do cabeçalho no início das páginas. Pode ser positivo ou negativo, passando assim a se sobrepor ao texto que não compõe o cabeçalho. Vale para para –footer-spacing \”valor\”;
  • –header-font-name \”font\”: muda o tipo de fonte do cabeçalho. Vale também para –footer-font-name \”font\”;
  • –header-font-size \”size\”: muda o tamanho da fonte a ser exibida no cabeçalho. Vale também para –footer-font-size \”size\”;
  • –footer-right \”Pag: [page] de [toPage]\”: exibe a numeração das páginas do arquivo pdf. Pode ser usado no cabeçalho assim como ter sua posição e modos de escrita alterados.

Dito isso, 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. Usaremos 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 criaremos as classes que iremos usar para gerar o relatório.

Assim vamos criar as seguintes classes: Cliente e Pedido. Criaremos 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, 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 está 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, e em seguida 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: página 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, passamos 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 código estamos informando:

  • ViewName: nome da view que será usada para gerar o PDF;
  • Model: nome do model usado na view. No exemplo, passamos a lista de clientes com PagedList;
  • IsGrayScale: indica se usamos uma escala de cinza.

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

Clique com o botão direito do mouse sobre o método Action RelatorioClientes, e a seguir clique em “Add View“, aceite o nome padrão e escolha o template List e Model Cliente, conforme 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 no link a seguir: