Reporte por Rango de Fechas y Periodos con Arquitectura en Capas, POO, C#, VB.Net, WinForm, SQL Server, SSRS- RDLC/ Nivel Avanzado

Introducción

Hola a todos, ahora veremos cómo crear reportes en una arquitectura en capas con Windows Forms, C#, VB.NET, SQL Server, SSRS (SQL Server Reporting Services – RDLC – Report Viewer) y Full Programación Orientada a Objetos.

En esta ocasión crearemos un reporte de ventas por rango de fechas y periodos, divididos en dos partes: un resumen general y las ventas a detalle, por ejemplo, las ventas de la fecha actual, los últimos 7 días, del mes actual, los últimos 30 días, del año actual o cualquier fecha personalizada. Ademas de agrupar las ventas netas por periodos, es decir, agrupar por días, semanas, meses o años, dependiendo al rango de fechas del reporte. Por otro lado las funciones de búsqueda de datos y exportar a PDF, Excel, Word o poder imprimir el reporte.

Observaciones

Reportes y Arquitectura en Capas

Ya mencioné en varios vídeos y artículos: La arquitectura en capas generalmente se divide en tres capas de aplicación y está destinada para aplicaciones complejas, puedes utilizar cualquier otra arquitectura según necesidades y complejidad de la aplicación, por ejemplo, si la aplicación es pequeña y con poca lógica de negocio bastará utilizar la arquitectura MVC, MVP, o uno personalizado. Solo recuerda en separar responsabilidades agrupar clases o componentes y organizar el código para obtener una buena estructura.

En este caso usaremos la arquitectura en capas ya que es la estructura más popular entre los programadores, bien recuerden que:

  • La capa de presentación solamente es responsable de manejar la interfaz gráfica de usuario como vistas renderizadas , archivos HTML, la interfaz de línea de comandos o los formularios de una aplicación de escritorio.
  • La capa de dominio o negocio es responsable de las reglas comerciales y el flujo de trabajo, que determina como se puede crear, validar, realizar cálculos, almacenar y cambiar los datos de un objeto comercial.
  • La capa de acceso a datos es responsable de almacenar u obtener datos desde un almacén de datos, como una base de datos, un servicio externo o un archivo plano.

El conjunto de datos, archivo y diseño de los reportes se crean en la capa de presentación (Visor de reportes y los archivos de datos del generador de informes). Sin embargo en muchas ocasiones cuando somos principiantes, accedemos a la base de datos y ejecutamos comandos SQL directamente desde los reportes de la capa de presentación, además de colocar algunas reglas comerciales como validaciones, cálculos complejos u otras actividades relacionado a negocios.

Esto es un grave error, estamos en contra de los principios de la arquitectura en capas y la programación orientada a objetos.

Acceder a la base de datos y ejecutar comandos SQL es responsabilidad de la capa de acceso a datos, y realizar cálculos o validaciones es responsabilidad de la capa de dominio, en cuanto al conjunto de datos, archivo y diseño de los reportes es responsabilidad de la capa de presentación . Del mismo modo si utilizas la arquitectura MVC, el modelo es quien se encarga de gestionar todos los datos e implementar las reglas del negocio.

Si bien es cierto que cualquier generador de reportes permite acceder a la base de datos y realizar validaciones y cálculos, entonces puedes usarlos con fines de prueba práctica o aplicaciones muy simples que trabajan a una sola capa.

Por otro lado, tener en cuenta que las imágenes mostradas de la arquitectura en capas es una representación gráfica muy superficial. En una aplicación muy compleja con muchas reglas de negocio, cada capa puede tener muchos componentes o sub-capas, además de agregar otras capas con sus propias responsabilidades, por ejemplo, la capa de aplicación, la capa de infraestructura, en ella la capa de soporte, acceso a datos, servicios externos o servicios de correo electrónico.

Recuerden que la estructura de las capas puede cambiar según necesidades y complejidad de la aplicación, en donde cada capa puede tener su propia arquitectura, cada componente puede tener su propio micro-arquitectura o cada clase o conjunto de clases puede tener su propia estructura, por ejemplo, puedes estructurar la capa de presentación utilizando el patrón modelo vista controlador (MVC) y si es una aplicación de escritorio, puedes usar el patrón modelo vista presentador (MVP). Pero a menudo, las aplicaciones que realizamos con fines de práctica o formación, son pequeñas, con poca lógica de negocio, entonces bastará utilizar únicamente estas tres capas, sin ningún componente, sub-capa o capas adicionales.

Tutorial

Bueno, espero que la introducción haya aclarado algunas cosas sobre crear reportes con un arquitectura en capas, ahora empecemos a crear la base de datos y aplicación.

Base de Datos


Tenemos 4 tablas relacionadas de una orden de venta  y los miles y miles de inserciones de datos. Puedes descargar desde el botón de abajo el script completo de la base de datos y así puedas y realizar la práctica.

Crear Base de Datos Relacional (BDR)

Diagrama

Script

create database BikeStore

create table products
(
	product_id int identity (1,1) primary key,
	product_name varchar (200) NOT NULL,
	model_year smallint NOT NULL,
	price decimal (10, 2) NOT NULL
);
CREATE TABLE customers (
	customer_id INT IDENTITY (1, 1) PRIMARY KEY,
	first_name VARCHAR (255) NOT NULL,
	last_name VARCHAR (255) NOT NULL,
	phone VARCHAR (25),
	email VARCHAR (255) NOT NULL,
	street VARCHAR (255),
	city VARCHAR (50),
	state VARCHAR (25),
	zip_code VARCHAR (5)
);
create table orders
(
	order_id int identity (1,1) primary key,
	customer_id int not null,
	order_date date not null,
	constraint FK_Customer foreign key (customer_id) references customers(customer_id)
);

create table order_items
(
	order_item_id int identity (1,1) primary key,
	order_id int not null,
	product_id INT NOT NULL,
	quantity INT NOT NULL,
	price DECIMAL (10, 2) NOT NULL,
	discount DECIMAL (4, 2) NOT NULL DEFAULT 0,
	constraint fk_Order foreign key (order_id) references orders(order_id),
	constraint fk_Product foreign key (product_id) references products(product_id)
);

Consulta SQL/Procedimiento – Reporte de Ventas Detallada por Rango de Fechas

Para realizar reportes, es simplemente hacer consultas a la base de datos, por ejemplo, un reporte de listado y stock de productos, reporte de productos que sean del modelo 2019, un reporte de clientes o un reporte general de ventas.

En este caso, en una única consulta , mostraremos un reporte de ventas a todo detalle, es decir, el ID de la venta, la fecha, el cliente, el nombre del producto, la cantidad del producto vendido y el monto total de la venta en un rango de fechas. Además de esta única consulta; desde la capa de dominio de la aplicación, agruparemos las ventas netas totales por períodos, ya sea por días, semanas, meses o años, dependiendo del rango de fecha.

Si deseas puedes crear un procedimiento almacenado para mostrar la consulta ( ver pestaña 2) e invocar el procedimiento desde la capa de acceso a datos de la aplicación.

select  o.order_id, 
		o.order_date,
		c.first_name+', '+c.last_name as customer, 
		products=stuff((select ' - ' +'x'+convert(varchar (10),oi2.quantity)+' '+ product_name 
          from order_items oi2
		   inner join products on products.product_id= oi2.product_id 
          where oi2.order_id = oi1.order_id
          for xml path('')), 1, 2, ''),
		sum((quantity*price)-discount)  as total_amount		
from orders o
inner join customers c on c.customer_id=o.customer_id 
inner join order_items oi1 on oi1.order_id =o.order_id 
where o.order_date between '2017/01/01' and '2017/12/30'
group by o.order_id, oi1.order_id, o.order_date, c.first_name, c.last_name  
order by o.order_id asc
create proc getSalesOrder
@fromDate Date,
@toDate Date
as
select  o.order_id, 
		o.order_date,
		c.first_name+', '+c.last_name as customer, 
		products=stuff((select ' - ' +'x'+convert(varchar (10),oi2.quantity)+' '+ product_name 
          from order_items oi2
		   inner join products on products.product_id= oi2.product_id 
          where oi2.order_id = oi1.order_id
          for xml path('')), 1, 2, ''),
		sum((quantity*price)-discount)  as total_amount		
from orders o
inner join customers c on c.customer_id=o.customer_id 
inner join order_items oi1 on oi1.order_id =o.order_id 
where o.order_date between @fromDate  and @toDate 
group by o.order_id, oi1.order_id, o.order_date, c.first_name, c.last_name  
order by o.order_id asc
go

Aplicación


Una vez creado la base de datos, crear las 4 tablas relacionadas, insertar los registros de la orden de venta, haber probado la consulta anterior para obtener las ventas a detalle o haber creado el procedimiento almacenado, pasemos a estructurar la aplicación.

Crear Proyecto en Capas – Visual Studio

  • Creamos una solución en blanco de Visual Studio (Archivo-> Nuevo-> Proyecto-> Plantillas-> Otros tipos de proyectos -> Soluciones de Visual Studio-> Solución en Blanco) y asígnele el nombre que desee.
  • Agregamos un proyecto nuevo de tipo Aplicación de Windows Forms para la Capa de Presentación.
  • Agregamos un proyecto nuevo de tipo Biblioteca de Clases para la Capa de Dominio o Negocio.
  • Agregamos un proyecto nuevo de tipo Biblioteca de Clases para la Capa de Acceso a Datos o Persistencia.

Agregar referencias entre capas (Dependecias)

  • Agregamos las referencias entre capas según la arquitectura tradicional en capas, donde una capa superior sólo conoce la capa inmediatamente debajo de ella.
  • En Capa de Presentación: Referenciamos a la Capa de Dominio.
  • En Capa de Dominio: Referenciamos a la Capa de Acceso a Datos.

Una vez creado y agregar las referencias entre capas, pasemos a codificar las capas.

Capa de acceso a Datos

Clase Conexión (ConnectionSql.cs/vb)

  • Agreguemos una clase abstracta para la conexión a SQL Server.
  • Importamos la librería System.Data.SqlClient.
  • Creamos un método protegido de tipo SqlConnection para obtener la conexión.
  • En el método creado, simplemente retornamos una instancia de conexión a sql, como parámetro, enviamos la cadena de conexión, servidor local, indicamos la base de datos, y nos conectamos mediante las credenciales de Windows.
    public abstract  class ConnectionSql
    {
        protected SqlConnection getConnection()
        {
            return new SqlConnection(
			"Server=(local); DataBase=BikeStore; integrated security=true"
			);
        }
    }
Public MustInherit Class ConnectionSql

    Protected Function getConnection() As SqlConnection
        Return New SqlConnection(
            "Server=(local); DataBase=BikeStore; integrated security=true")
    End Function

End Class

Clase Objeto de Acceso a Datos Orden (OrderDao.cs)

  • Agregamos otra clase publica para el objeto de acceso a datos de la entidad órdenes de venta.
  • Indicamos que la clase hereda de la clase conexión a SQL.
  • Importamos las librería System.Data y System.DataSqlClient.
  • Creamos un método de tipo DataTable o List<Object> para obtener las órdenes de venta por rango de fecha, para ello creamos 2 parámetros,una para la fecha de inicio y la otra para fecha final de la consulta.
  • Agregamos lo siguiente códigos para realizar la consulta.
    public class OrderDao:ConnectionSql 
    {
        public DataTable getSalesOrder(DateTime fromDate, DateTime toDate)
        {
            using (var connection = getConnection())
            {
                connection.Open();

                using (var command = new SqlCommand())
                {
                    command.Connection = connection;
                    command.CommandText = @"getSalesOrder";
                    command.Parameters.AddWithValue("@fromDate", fromDate );
                    command.Parameters.AddWithValue("@toDate", toDate );
                    command.CommandType = CommandType.StoredProcedure;
                    var reader = command.ExecuteReader();
                    var table = new DataTable();
                    table.Load(reader );
                    reader.Dispose();

                    return table;
                }
            }
        }
    }
	public DataTable getSalesOrder(DateTime fromDate, DateTime toDate)
	{
		using (var connection = getConnection())
		{
			connection.Open();
			using (var command = new SqlCommand())
			{
				command.Connection = connection;
				command.CommandText = @"select  o.order_id, 
												o.order_date,
												c.first_name+', '+c.last_name as customer, 
												products=stuff((select ' , ' +'x'+convert(varchar (10),oi2.quantity)+' '+ product_name 
												  from order_items oi2
												   inner join products on products.product_id= oi2.product_id 
												  where oi2.order_id = oi1.order_id
												  for xml path('')), 1, 2, ''),
												sum((quantity*price)-discount)  as total_amount		
										from orders o
										inner join customers c on c.customer_id=o.customer_id 
										inner join order_items oi1 on oi1.order_id =o.order_id 
										where o.order_date between @fromDate and @toDate
										group by o.order_id, oi1.order_id, o.order_date, c.first_name, c.last_name  
										order by o.order_id asc";
				command.CommandType = CommandType.Text;
				command.Parameters.Add("@fromDate", SqlDbType.Date).Value=fromDate;
				command.Parameters.Add("@toDate", SqlDbType.Date).Value = toDate;
				SqlDataReader  reader = command.ExecuteReader();
				var table = new DataTable();
				table.Load(reader);
				reader.Dispose();
				return table;
			}
		}
	}
}
Public Class OrderDao
    Inherits ConnectionSql

    Public Function getSalesOrder(fromDate As DateTime, toDate As DateTime) As DataTable
        Using connection = getConnection()
            connection.Open()
            Using command = New SqlCommand()
                command.Connection = connection
                command.CommandText = "getSalesOrder"
                command.Parameters.AddWithValue("@fromDate", fromDate)
                command.Parameters.AddWithValue("@toDate", toDate)
                command.CommandType = CommandType.StoredProcedure
                Dim reader = command.ExecuteReader()
                Dim table = New DataTable()
                table.Load(reader)
                reader.Dispose()
                Return table
            End Using
        End Using
    End Function
End Class
Public Function getSalesOrder(fromDate As DateTime, toDate As DateTime) As DataTable
    Using connection = getConnection()
        connection.Open()

        Using command = New SqlCommand()
            command.Connection = connection
            command.CommandText = "select  o.order_id, 
												o.order_date,
												c.first_name+', '+c.last_name as customer, 
												products=stuff((select ' , ' +'x'+convert(varchar (10),oi2.quantity)+' '+ product_name 
												  from order_items oi2
												   inner join products on products.product_id= oi2.product_id 
												  where oi2.order_id = oi1.order_id
												  for xml path('')), 1, 2, ''),
												sum((quantity*price)-discount)  as total_amount		
										from orders o
										inner join customers c on c.customer_id=o.customer_id 
										inner join order_items oi1 on oi1.order_id =o.order_id 
										where o.order_date between @fromDate and @toDate
										group by o.order_id, oi1.order_id, o.order_date, c.first_name, c.last_name  
										order by o.order_id asc"
            command.CommandType = CommandType.Text
            command.Parameters.Add("@fromDate", SqlDbType.Date).Value = fromDate
            command.Parameters.Add("@toDate", SqlDbType.Date).Value = toDate
            Dim reader As SqlDataReader = command.ExecuteReader()
            Dim table = New DataTable()
            table.Load(reader)
            reader.Dispose()
            Return table
        End Using
    End Using
End Function

Capa de Dominio – Negocio

Ahora codificaremos la capa de dominio o negocio, agregamos las siguientes clases/Objetos para guardar el listado de ventas a detalle y para agrupar las ventas netas por periodo.

Clase Listado de Ventas (SalesListing.cs)

    public class SalesListing
    {
        public int orderId { get; set; }
        public DateTime orderDate { get; set; }
        public string customer { get; set; }
        public string products { get; set; }
        public double totalAmount { get; set; }
    }
Public Class SalesListing
    Public Property orderId As Integer
    Public Property orderDate As DateTime
    Public Property customer As String
    Public Property products As String
    Public Property totalAmount As Double
End Class

Clase Ventas Netas Por Periodo (NetSalesByPeriod.cs)

    public class NetSalesByPeriod
    {
        public string period { get; set; }
        public double netSales { get; set; }
    }
Public Class NetSalesByPeriod
    Public Property period As String
    Public Property netSales As Double
End Class

Clase Reporte de Ventas (SalesReport.cs)

  • Agregamos una clase para el reporte de ventas.
  • Importamos la capa de acceso a datos
  • Declaramos los atributos y propiedades necesarios para el reporte.
  • Declaramos una lista de objetos de tipo listado de ventas para almacenar la consulta de la base de datos.
  • Declaramos una lista de objetos de tipo ventas netas por periodo para agrupar las ventas netas por periodo según rango de fechas del reporte.
  • Finalmente creamos un método para crear el reporte e inicializar los objetos.
public class SalesReport
{
	//Attributes-Properties
	public DateTime reportDate { get; private set; }
	public DateTime startDate { get; private set; }
	public DateTime endDate{ get; private set; }
	public List <SalesListing> salesListing { get; private set; }
	public List<NetSalesByPeriod> netSalesByPeriod { get; private set; }
	public double totalNetSales { get; private set; }

	//Methods
	public void createSalesOrderReport(DateTime fromDate, DateTime toDate)
	{
		//implement dates
		reportDate = DateTime.Now;
		startDate = fromDate;
		endDate = toDate;
		//create sales listing
		var orderDao = new OrderDao();
		var result = orderDao.getSalesOrder(fromDate,toDate );

		salesListing = new List<SalesListing>();

		foreach (System.Data.DataRow rows in result.Rows)
		{
			var salesModel = new SalesListing()
			{
				orderId = Convert.ToInt32(rows[0]),
				orderDate = Convert.ToDateTime(rows[1]),
				customer = Convert.ToString(rows[2]),
				products = Convert.ToString(rows[3]),
				totalAmount = Convert.ToDouble(rows[4])
			};
			salesListing.Add(salesModel);

			//calculate total net sales
			totalNetSales += Convert.ToDouble(rows[4]);
		}
		//create net sales by period
		////create temp list net sales by date
		var listSalesByDate = (from sales in salesListing
							   group sales by sales.orderDate
							 into listSales
							   select new
							   {
								   date = listSales.Key,
								   amount = listSales.Sum(item => item.totalAmount)
							   }).AsEnumerable();
		////get number of days
		int totalDays = Convert.ToInt32((toDate-fromDate ).Days);

		////group period by days
		if (totalDays <= 7)
		{
			netSalesByPeriod = (from sales in listSalesByDate
								group sales by sales.date.ToString("dd-MMM-yyyy")
							   into listSales
								select new NetSalesByPeriod
								{
									period = listSales.Key,
									netSales = listSales.Sum(item => item.amount)
								}).ToList();
		}
		////group period by weeks
		else if (totalDays <= 30)
		{
			netSalesByPeriod = (from sales in listSalesByDate
								group sales by
								System.Globalization.CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(
									sales.date, System.Globalization.CalendarWeekRule.FirstDay, DayOfWeek.Monday)
							into listSales
								select new NetSalesByPeriod
								{
									period = "Week " + listSales.Key.ToString(),
									netSales = listSales.Sum(item => item.amount)
								}).ToList();
		}
		////group period by months
		else if (totalDays <= 365)
		{
			netSalesByPeriod = (from sales in listSalesByDate
								group sales by sales.date.ToString("MMM-yyyy")
							into listSales
								select new NetSalesByPeriod
								{
									period = listSales.Key,
									netSales = listSales.Sum(item => item.amount)
								}).ToList();
		}
		////group period by years
		else {
			netSalesByPeriod = (from sales in listSalesByDate
								group sales by sales.date.ToString("yyyy")
							into listSales
								select new NetSalesByPeriod
								{
									period = listSales.Key,
									netSales = listSales.Sum(item => item.amount)
								}).ToList();
		}
	}

}
Public Class SalesReport

    'Attributes-Properties
    Public Property reportDate As Date
    Public Property startDate As Date
    Public Property endDate As Date
    Public Property salesListing As List(Of SalesListing)
    Public Property netSalesByPeriod As List(Of NetSalesByPeriod)
    Public Property totalNetSales As Double

    'Methods
    Public Sub createSalesOrderReport(fromDate As Date, toDate As Date)
        'implement dates
        reportDate = Date.Now
        startDate = fromDate
        endDate = toDate

        'create sales listing
        Dim orderDao = New OrderDao
        Dim result = orderDao.getSalesOrder(fromDate, toDate)
        salesListing = New List(Of SalesListing)

        For Each row As DataRow In result.Rows
            Dim salesModel = New SalesListing With {
                .orderId = Convert.ToInt32(row(0)),
                .orderDate = Convert.ToDateTime(row(1)),
                .customer = Convert.ToString(row(2)),
                .products = Convert.ToString(row(3)),
                .totalAmount = Convert.ToDouble(row(4))
            }
            salesListing.Add(salesModel)

            'calculate total net sales
            totalNetSales += Convert.ToDouble(row(4))
        Next

        'create net sales by period
        'create temp list net sales by date
        Dim listSalesByDate = (From sales In salesListing
                               Group sales By keyDateGroup = sales.orderDate
                               Into listSales =
                               Group Select New With {
            .date = keyDateGroup,
            .amount = listSales.Sum(Function(item) item.totalAmount)
        }).AsEnumerable

        '////get number of days
        Dim totalDays = Convert.ToInt32((toDate - fromDate).Days)

        '////group period by days
        If totalDays <= 7 Then
            netSalesByPeriod = (From sales In listSalesByDate
                                Group sales By keyDateGroup = sales.date.ToString("dd-MMM-yyyy")
                                    Into listSales =
                                    Group Select New NetSalesByPeriod With {
                .period = keyDateGroup,
                .netSales = listSales.Sum(Function(item) item.amount)
            }).ToList

            '////group period by weeks
        ElseIf totalDays <= 30 Then
            netSalesByPeriod = (From sales In listSalesByDate
                                Group sales By keyDateGroup =
                                    System.Globalization.CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(
                                    sales.date, System.Globalization.CalendarWeekRule.FirstDay, DayOfWeek.Monday)
                                    Into listSales =
                                    Group Select New NetSalesByPeriod With {
                .period = "Week " & keyDateGroup.ToString,
                .netSales = listSales.Sum(Function(item) item.amount)
            }).ToList

            '////group period by months
        ElseIf totalDays <= 365 Then
            netSalesByPeriod = (From sales In listSalesByDate
                                Group sales By keyDateGroup = sales.date.ToString("MMM-yyyy")
                                    Into listSales =
                                    Group Select New NetSalesByPeriod With {
                .period = keyDateGroup,
                .netSales = listSales.Sum(Function(item) item.amount)
            }).ToList

            '////group period by years
        Else
            netSalesByPeriod = (From sales In listSalesByDate
                                Group sales By keyDateGroup = sales.date.ToString("yyyy")
                                    Into listSales =
                                    Group Select New NetSalesByPeriod With {
                .period = keyDateGroup,
                .netSales = listSales.Sum(Function(item) item.amount)
            }).ToList
        End If

    End Sub
End Class

Capa de Presentación

Una vez terminado de codificar la capa de dominio, ahora agregaremos los conjuntos de datos para el reporte, crearemos el archivo de reporte y agregaremos un formulario para inicializar el visor de reporte y crear los botones con un rango de fechas en específicos o personalizado.

Generador de Reportes

Puedes utilizar cualquier generador de reportes, ya sea Crystal Reports, Report Viewer-SSRS, Component One, DevExpress, Jasper Reports o cualquier otro, ya que simplemente lo usaremos como diseñador y visor de reportes, y la capa de dominio ya dispone de todos los datos listos para ser mostrados.

En este caso usaremos el control REPORT VIEWER con modo de procesamiento local (RDLC) perteneciente a SSRS de Microsoft (SQL Server Reporting Services), personalmente prefiero este generador de informes porque es muy flexible, ligero, rápido, gratuito y muy fácil de usar e integrar en tu aplicación de .NET.

Reporting Services con Report Viewer-RDLC

Report Viewer es un control para visual studio, que permite visualizar reportes RDL y RDLC en nuestra aplicación web o de escritorio.

  • RDLC (Lenguaje de Definición de Informes del lado del Cliente), se utiliza para crear archivos de «informes locales» en Visual Studio. Estos archivos se retienen localmente y no se almacenan en un servidor SSRS, es decir, se ejecuta en el lado de cliente a partir de un conjunto de datos.
  • RDL, son archivos de informes que están diseñados para usarse con SQL Server Reporting Services (SSRS), y se almacenan en el servidor de informes y se ejecutan desde allí.

En este tutorial utilizaremos Reporting Services con modo de procesamiento local (RDLC) y mostraremos el informe mediante el control Report Viewer. A partir de Visual Studio 2015, este control y los servicios de reporte local ya no están integrados en la instalación por defecto de Visual Studio, tienes que instalarlo desde Nuget o Extensiones de Visual Studio.

Archivo de Informes de Ventas (SalesReport.rdlc)

  • Agregamos el archivo / diseñador de reportes local RDLC en blanco, si deseas puedes usar el asistente de informes.
  • Compilamos la capa de presentación para cargar las referencias.
  • Ajustamos el tamaño de pagina del informe (en este caso A4->21cm x 29.7 cm) y el cuerpo del informe (21cm x 15cm, el alto del cuerpo debe ser ajustado a los componentes del informe).
  • Agregamos los objetos (clases) Reporte de ventas (salesReport), Listado de Ventas (salesListing) y Ventas netas por periodo (netSalesByPeriod) al conjunto de datos del reporte local.
  • Finalmente diseñamos el informe de la siguiente manera y en ello, agregamos los campos a mostrar (Campos de nuestros objetos de reporte).

Diseño de Formulario

  • Una vez terminado de diseñar el reporte local, agregamos los botones con fechas especificas para el reporte y 2 controles selector de fecha (DateTimePicker) para el reporte de fechas personalizados.
  • Agregamos el control Report Viewer y acoplamos a todo el contenedor.
  • En el visor de reporte, elegimos el archivo del reporte a mostrar, en este caso, el reporte de ventas (SalesReport.rdlc)
  • Una vez elegido el reporte, en la parte baja se creará los 3 fuentes enlace a datos para los 3 objetos del conjunto de datos del informe.

Código de Formulario

  • Importamos la capa de dominio o negocio.
  • Creamos el método obtener reporte de ventas (getSalesReport), con 2 parámetros para la fecha de inicio y final del reporte.
  • En el método, instanciamos a la clase reporte de venta de la capa de dominio e invocamos el método crear reporte de ordenes de venta.
  • Inicializamos la fuente de datos del enlace de datos del visor de reporte (SalesReportBindingSource, SalesListingBindingSource, NetSalesByPeriodBindingSource) con el objeto reporte y sus propiedades (reportModel, reportModel.salesListing, reportModel.netSalesByPeriod).
  • Finalmente invocamos el método anterior desde los botones con los respectivos rango de fechas.
public partial class Form1 : Form
{
	public Form1()
	{
		InitializeComponent();
	}

	private void Form1_Load(object sender, EventArgs e)
	{
	   
	}

	private void getSalesReport(DateTime startDate, DateTime endDate)
	{
		SalesReport reportModel = new SalesReport();
		reportModel.createSalesOrderReport(startDate, endDate);

		SalesReportBindingSource.DataSource = reportModel;
		SalesListingBindingSource.DataSource = reportModel.salesListing;
		NetSalesByPeriodBindingSource.DataSource = reportModel.netSalesByPeriod;

		this.reportViewer1.RefreshReport();
	}

	private void btnToday_Click(object sender, EventArgs e)
	{
		var fromDate = DateTime.Today;
		var toDate = DateTime.Now;

		getSalesReport(fromDate,toDate);
	}

	private void btnLast7Days_Click(object sender, EventArgs e)
	{
		var fromDate = DateTime.Today.AddDays(-7);
		var toDate = DateTime.Now;

		getSalesReport(fromDate, toDate);
	}

	private void btnThisMonth_Click(object sender, EventArgs e)
	{
		var fromDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
		var toDate = DateTime.Now;

		getSalesReport(fromDate, toDate);
	}

	private void btnLast30Days_Click(object sender, EventArgs e)
	{
		var fromDate = DateTime.Today.AddDays(-30);
		var toDate = DateTime.Now;

		getSalesReport(fromDate, toDate);
	}

	private void btnThisYear_Click(object sender, EventArgs e)
	{
		var fromDate = new DateTime(DateTime.Now.Year, 1, 1);
		var toDate = DateTime.Now;

		getSalesReport(fromDate, toDate);
	}

	private void btnApplyCustomDate_Click(object sender, EventArgs e)
	{
		var fromDate = dateTimePickerFromDate.Value;
		var toDate = dateTimePickerToDate.Value;

		getSalesReport(fromDate, new DateTime(toDate.Year,toDate.Month, toDate.Day,23,59,59));
	}
}
Public Class Form1

    Private Sub getSalesReport(ByVal startDate As DateTime, ByVal endDate As DateTime)
        Dim reportModel As SalesReport = New SalesReport()
        reportModel.createSalesOrderReport(startDate, endDate)
        SalesReportBindingSource.DataSource = reportModel
        SalesListingBindingSource.DataSource = reportModel.salesListing
        NetSalesByPeriodBindingSource.DataSource = reportModel.netSalesByPeriod
        Me.ReportViewer1.RefreshReport()
    End Sub

    Private Sub btnToday_Click(sender As Object, e As EventArgs) Handles btnToday.Click
        Dim fromDate = DateTime.Today
        Dim toDate = DateTime.Now
        getSalesReport(fromDate, toDate)
    End Sub

    Private Sub btnLast7Days_Click(sender As Object, e As EventArgs) Handles btnLast7Days.Click
        Dim fromDate = DateTime.Today.AddDays(-7)
        Dim toDate = DateTime.Now
        getSalesReport(fromDate, toDate)
    End Sub

    Private Sub btnThisMonth_Click(sender As Object, e As EventArgs) Handles btnThisMonth.Click
        Dim fromDate = New DateTime(DateTime.Now.Year, DateTime.Now.Month, 1)
        Dim toDate = DateTime.Now
        getSalesReport(fromDate, toDate)
    End Sub

    Private Sub btnLast30Days_Click(sender As Object, e As EventArgs) Handles btnLast30Days.Click
        Dim fromDate = DateTime.Today.AddDays(-30)
        Dim toDate = DateTime.Now
        getSalesReport(fromDate, toDate)
    End Sub

    Private Sub btnThisYear_Click(sender As Object, e As EventArgs) Handles btnThisYear.Click
        Dim fromDate = New DateTime(DateTime.Now.Year, 1, 1)
        Dim toDate = DateTime.Now
        getSalesReport(fromDate, toDate)
    End Sub

    Private Sub btnApplyCustomDate_Click(sender As Object, e As EventArgs) Handles btnApplyCustomDate.Click
        Dim fromDate = dateTimePickerFromDate.Value
        Dim toDate = dateTimePickerToDate.Value
        getSalesReport(fromDate, New DateTime(toDate.Year, toDate.Month, toDate.Day, 23, 59, 59))
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        Me.ReportViewer1.RefreshReport()
    End Sub

End Class

Y eso es todo 🙂

Ver video tutorial

Descargas