Introducción
Hola, en esta ocasión realizaremos un reporte de los productos más vendidos por rango de fechas, por ejemplo, los últimos 7 días, el mes actual, o fechas personalizadas. Usaremos la arquitectura en capas y programación orientada a objetos, con visual basic, Windows Form, el control report viewer, el diseñador de informes local RDLC y SQL Server.

Tutorial
Bien, 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 el script completo de la base de datos desde el botón de abajo y así puedas 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 los Productos más vendidos por Rango de Fechas
En la siguiente consulta, mostraremos el ID del producto, el Nombre del producto, el Precio base unitario del producto, la cantidad vendida y el importe de las ganancias del producto.
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 top 10 p.product_id, p.product_name, p.price as unitBasePrice, sum(oi.quantity) as quantitySold, sum((oi.price*oi.quantity)-oi.discount) as totalAmount from orders o inner join order_items oi on oi.order_id=o.order_id inner join products p on p.product_id=oi.product_id where o.order_date between '01-01-2018' and '30-12-2019 23:59:59' group by p.product_id, p.product_name, p.price order by quantitySold desc
create proc GetBestSellingProducts @startDate Date, @endDate Date as select top 10 p.product_id, p.product_name, p.price as unitBasePrice, sum(oi.quantity) as quantitySold, sum((oi.price*oi.quantity)-oi.discount) as totalAmount from orders o inner join order_items oi on oi.order_id=o.order_id inner join products p on p.product_id=oi.product_id where o.order_date between @startDate and @endDate group by p.product_id, p.product_name, p.price order by quantitySold desc 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.vb)
- Agreguemos una clase abstracta (MustInherit) 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.
Imports System.Data.SqlClient
Public MustInherit Class ConnectionSQL
Protected Function GetConnection() As SqlConnection
Return New SqlConnection("Server=(local); DataBase=BikeStore; integrated security=true")
End Function
End ClassClase Objeto de Acceso a Datos Orden (OrderDao.vb)
- 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.DataSqlClient.
- Creamos un método de tipo DataTable o List<Object> para obtener los productos mas vendidos 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.
Imports System.Data.SqlClient
Public Class OrderDao
Inherits ConnectionSQL
Public Function GetBestSellingProducts(fromDate As DateTime, toDate As DateTime) As DataTable
Using connection = GetConnection()
connection.Open()
Using command = New SqlCommand()
command.Connection = connection
command.CommandText = "GetBestSellingProducts"
command.Parameters.AddWithValue("@startDate", fromDate)
command.Parameters.AddWithValue("@endDate", 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 ClassImports System.Data.SqlClient
Public Class OrderDao
Inherits ConnectionSQL
Public Function GetBestSellingProducts(fromDate As DateTime, toDate As DateTime) As DataTable
Using connection = GetConnection()
connection.Open()
Using command = New SqlCommand()
command.Connection = connection
command.CommandText = "select top(10) oi.product_id, p.product_name,
p.price as unitBasePrice,
sum(quantity) as quantity,
sum((oi.price*oi.quantity)-oi.discount)as amount
from
order_items oi
inner join orders o on o.order_id=oi.order_id
inner join products p on p.product_id=oi.product_id
where o.order_date between @startDate and @endDate
group by
oi.product_id, p.product_name,p.price
order by quantity desc"
command.Parameters.Add("@startDate", SqlDbType.Date).Value = fromDate
command.Parameters.Add("@endDate", SqlDbType.Date).Value = toDate
command.CommandType = CommandType.Text
Dim reader = command.ExecuteReader()
Dim table = New DataTable()
table.Load(reader)
reader.Dispose()
Return table
End Using
End Using
End Function
End ClassCapa de Dominio – Negocio
Ahora codificaremos la capa de dominio o negocio, agregamos las siguientes clases/Objetos para guardar el listado de los productos mas vendidos:
Clase Mejores productos (BestProducts.vb)
Public Class BestProducts
Public Property ProductId As Integer
Public Property ProductName As String
Public Property Price As Double
Public Property Quantity As Integer
Public Property Amount As Double
End ClassClase Reporte de Ventas (SalesReport.vb)
- 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 mejores productos (BestProducts.vb) para almacenar la consulta de la base de datos.
- Finalmente creamos un método para crear el reporte e inicializar los objetos.
Imports DataAccess
Public Class SalesReport
'Attributes
Private _reportDate As DateTime
Private _startDate As DateTime
Private _endDAte As DateTime
Private _listBestProducts As List(Of BestProducts)
Private _total As Double
'Propiertes
Public Property ReportDate As Date
Get
Return _reportDate
End Get
Private Set(value As Date)
_reportDate = value
End Set
End Property
Public Property StartDate As Date
Get
Return _startDate
End Get
Private Set(value As Date)
_startDate = value
End Set
End Property
Public Property EndDAte As Date
Get
Return _endDAte
End Get
Private Set(value As Date)
_endDAte = value
End Set
End Property
Public Property ListBestProducts As List(Of BestProducts)
Get
Return _listBestProducts
End Get
Private Set(value As List(Of BestProducts))
_listBestProducts = value
End Set
End Property
Public Property Total As Double
Get
Return _total
End Get
Private Set(value As Double)
_total = value
End Set
End Property
'Constructors
'Methods
Public Sub GetBestSellingProductsReport(fromDate As DateTime, toDate As DateTime)
'implement dates
ReportDate = Date.Now()
StartDate = fromDate
EndDAte = toDate
'Create list best selling products
ListBestProducts = New List(Of BestProducts)()
Dim orderDao = New OrderDao()
Dim resultTable = orderDao.GetBestSellingProducts(fromDate, toDate)
For Each row As DataRow In resultTable.Rows
Dim bestProductModel = New BestProducts() With
{
.ProductId = Convert.ToInt32(row(0)),
.ProductName = Convert.ToString(row(1)),
.Price = Convert.ToDouble(row(2)),
.Quantity = Convert.ToInt32(row(3)),
.Amount = Convert.ToDouble(row(4))
}
ListBestProducts.Add(bestProductModel)
Total += Convert.ToDouble(row(4))
Next
End Sub
End ClassCapa 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, Microsoft RDLC Report Designer, 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 tutorial utilizaremos Reporting Services con modo de procesamiento local (RDLC) y mostraremos el informe mediante el control Report Viewer. A partir de Visual Studio 2017, 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. Puede ver este tutorial para realizar la instalacion.
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.vb) y los productos mas vendidos (BestProducts.vb) 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 los productos mas vendidos (BestProductsReport.rdlc)
- Una vez elegido el reporte, en la parte baja se creará los 2 fuentes enlace a datos para los 2 objetos del conjunto de datos del informe (SalesReportDt y BestProductsDT).

Código de Formulario
- Importamos la capa de dominio o negocio.
- Creamos el método cargar reporte de los productos más vendidos (LoadBestProductsReport), 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 obtener reporte de los productos más vendidos.
- Inicializamos la fuente de datos del enlace de datos del visor de reporte (SalesReportBindingSource, BestProductsBindingSource) con el objeto reporte y su propiedad (salesOrderModel, salesOrderModel.ListBestProducts).
- Finalmente invocamos el método anterior desde los botones con los respectivos rango de fechas.
Imports Domain
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub LoadBestProductsReport(startDate As DateTime, endDate As DateTime)
Dim salesOrderModel = New SalesReport()
salesOrderModel.GetBestSellingProductsReport(startDate, endDate)
SalesReportBindingSource.DataSource = salesOrderModel
BestProductsBindingSource.DataSource = salesOrderModel.ListBestProducts
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
LoadBestProductsReport(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
LoadBestProductsReport(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
LoadBestProductsReport(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
LoadBestProductsReport(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
LoadBestProductsReport(fromDate, toDate)
End Sub
Private Sub btnApply_Click(sender As Object, e As EventArgs) Handles btnApply.Click
Dim fromDate = DateTimePickerFromDate.Value
Dim toDate = DateTimePickerToDate.Value
LoadBestProductsReport(fromDate, New DateTime(toDate.Year, toDate.Month, toDate.Day, 23, 59, 59))
End Sub
End ClassY eso es todo 🙂

Los comentarios están cerrados.