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:
- Mvc5_RelatoriosPDF.zip (sem as referências e os arquivos da pasta rotativa).