Un lugar por donde empezar...

Creando Reporte Cabecera-Detalle (usando Shape)

Este post no está necesariamente vinculado al Firebird, esta mas bien relacionado al ADO. Lo escribo aquí porque en diversos foros he encontrado que este tema les resulta confuso a diversos foristas y con esto post pretendo (ojala lo logre) aclarar el tema. Empezamos.

¿Que es un reporte Cabecera – Detalle?
Es también llamado Maestro-Detalle y se aplica también a los informes, básicamente se pretende mostrar registros que tienen a su vez “sub-registros” relacionados a el, estos “sub-registros” relacionados al registro principal (cabecera o maestro) son registros que detallan o complementan la información del registro principal o en todo caso nos muestra su pertenencia. Por cierto que esto puede tener mas niveles pero aqui solo trataremos un nivel.

¿Podría aplicarse a una Factura este principio?
Definitivamente si, ya que este documento está compuesto por datos cabecera (Nombre de cliente, numero de factura, fecha, etc) y por datos detalle (cantidad, nombre del producto, precio unitario). Por supuesto que esto también se aplica a las boletas de venta, tickets, recibo por honorarios, notas de crédito, débito, etc.

¿Como lo genero?
Para generar este documento o reporte, necesitamos que existan al menos dos tablas en la base de datos que contengan esta información, una guardara los datos de cabecera y la otra los datos de detalle, ambas deben estar relacionados por algún campo identificador, ademas estas tablas pudieran estar relacionados a otras tablas propias del diseño del sistema que manejan. Entonces, haremos una consulta jerárquica hacia esas dos tablas la cual almacenaremos en un recordset y luego poblamos al reporteador (en este post usaremos el DataReport que viene en Visual Basic 6).

¿Y por qué no puedo usar una consulta simple?
Una consulta simple cuyo resultado almacenamos en un recordset esta formado por filas y columnas, consideremos lo como una tabla en memoria que contiene registros obtenidos como resultado de una consulta SQL que hallamos ejecutado.

Una consulta jerárquica esta compuesta por digamos “dos tablas” en memoria y que están relacionadas en la consulta, esta consulta almacenada en un solo recordset esta a la espera de almacenar dicha información en el objeto que pueda recibirlo, por ejemplo, el control MSHFlexgrid (H de Hierarchy=Jerarquia) o el DataReport.

Con una consulta simple no es posible mostrar detalle, y si lo fuera seria real y absurdamente tedioso.

¿Como construyo esta consulta?
Basicamente armaremos 2 consultas simples y las unificaremos usando el comando SHAPE, esto se almacenará en un recordset y luego este recordset lo conectamos al DataReport y listo.

Bien, vamos a practicar creando una factura. Para esto necesitamos una tabla cabecera de la factura y otra tabla que contendrá el detalle de dicha factura. Llamemos a la tabla cabecera CAB y la de detalle DET (usaremos pocos campos ya que esto va como un ejemplo)

Tabla CAB

Cab2

Haz clic para verlo mas grande

 

Tabla DET

DET

Haz clic para verlo mas grande

Bien, con estas dos tablas tenemos para formar la factura. Bien, supongamos que queremos ver la factura 456 (campo CAB_NUMDOC de la tabla CAB), esta factura tiene como Id de cabecera el numero 4, este numero 4 se encuentra en el campo DET_CAB_ID de la tabla DET en dos registros, quiere decir que esta factura tiene 2 items como su puede ver en las imágenes. Por tanto, el parámetro que pasaremos sera el numero de factura 456.

Ahora haremos la consulta, naturalmente ya habrán establecido la conexión a la base de datos, sino puede checar en este post como se establece la conexión.

Escribiremos esta consulta a la tabla CAB dentro de una función que llamaremos sqlC:

Private Function sqlC(Id As Integer)
sqlC = “Select CAB_ID, CAB_NOMCLI, CAB_RUC, CAB_FECHA FROM CAB ”
If Id > 0 Then sqlC = sqlC & “Where CAB_NUMDOC = ” & Id
End Function

Si en Id no va ningún valor entonces el sql no aplicara el Where y por lo tanto mostrara todos los registros de la tabla CAB. Como en este caso le pasaremos el valor 456 en el parámetro Id entonces obtendremos 1 solo registro.

… Y ¿para que meterlo en una función? Muchas veces lo hago por orden, pero principalmente porque estas funciones se pueden reutilizar, mas adelante podria querer un reporte de facturas emitidas pero queriendo ver solo las cabeceras y no los detalles, esta funcion me puede servir en ese caso.

Ahora escribiremos la consulta a la tabla DET dentro de una funcion que llamaremos sqlD

Private Function sqlD()
sqlD = _
“Select DET_ID, DET_CAB_ID, DET_CANT, DET_PROD, DET_PRECIO,” & _
“(DET_CANT * DET_PRECIO) AS SUBT FROM DET”
End Function

Aqui estamos obteniendo todos los registros de la tabla DET ya que lo filtraremos después, en esta parte por performance puede ser interesante filtrar también en la tabla DET los registros para hacer la consulta mas veloz (dependiendo claro de la definición de indices), de momento lo dejaremos asi, la consulta ademas esta multiplicando los campos de cantidad y precio para tener un subtotal.

Ahora lo que sigue es la función que une ambas consultas, esta funcion la llamaremos sqlCD (CD de cabecera-detalle, en todos los casos pueden usar los nombres que mas les acomode):

Private Function sqlCD(Id As Integer)
sqlCD = _
“SHAPE {” & sqlC(Id) & “} as Cabecera ” & _
“APPEND ({” & sqlD & “} as Detalle ” & _
“RELATE CAB_ID to DET_CAB_ID) as Deta”
End Function

Lo que hace este código es que unirá el resultado de la consulta sqlD a sqlC relacionándolos por el campo CAB_ID de la tabla CAB con el campo DET_CAB_ID de la tabla DET, a este sub-resultado lo llamaremos Deta que luego lo usaremos en el reporte para poblar el detalle.

Ahora, para ejecutar todo lo de arriba usamos este codigo (supongamos que lo tenemos en un boton llamado btnReporte)

Private Sub btnReporte_Click()
Dim rsCD As New ADODB.Recordset

rsCD.Open sqlCD(Val(txtID)), dB, 1, 1
Set rptMiReporte.DataSource = rsCD
rptMiReporte.Show
End Sub

Bien, en la primera linea declaramos el recordset rsCD como habitualmente lo hacemos. En la siguiente linea realizamos la consulta pasando como parametro el valor que tengamos en el textbox txtID, es en ese textbox donde escribiremos el numero 456 (u otro) para ejecutar la consulta. dB es el conector que declare previamente, y 1,1 es el cursor y el tipo de bloqueo. Esa consulta se almacena en el recordset rsCD y luego se la asignamos al reporte rptMiReporte y en la siguiente linea lo mostramos.

Ahora veamos como preparamos el reporte. Agregamos un reporte al proyecto y lo nombramos rptMiReporte:

rep1

Luego hacemos clic derecho sobre el reporte y en el menu emergente seleccionamos “Insertar encabezado o pie de grupo”

rep2

Luego agregamos los controles al reporte:

REP3

En cada control rptTextBox deberá poner en la propiedad DataField el nombre del campo NO DE LA TABLA sino del recordset, el reporte no se conecta a la tabla directamente sino al recordset que tiene el resultado de la consulta. Presten especial cuidado con el area de detalle porque los controles de esa area ademas de tener definido el DataField también debe definirse el campo DataMember como Deta que es con el nombre que definimos en la consulta, si usan otro nombre recuerden modificar aquí también.

REP4

Y con eso concluimos la factura (en su forma basica claro), al ejecutar el programa tendremos la factura asi:

REP5

 

¿Y que pasa si no le damos ningun valor? Obtendremos este reporte.

REP6

Naturalmente esto no es una factura solo estoy mostrando las posibilidades, hay que hacer validaciones, restricciones, etc, lo que les muestro aquí es un ejemplo básico que pueda servir como punto de partida.

Si alguien me pregunta ¿como usar este código para mostrar las facturas emitidas en un rango de fechas?

Respuesta: Pasen como parámetros las dos fechas con la cual filtraran en CAB y la vinculación seguirá siendo por el campo de relación. Obviamente tienen que modificar o crear un DataReport diseñándolo debidamente para que se muestre como un reporte tal cual.

Espero haber sido claro, estoy adjuntando el código, ciertamente alli lleva una BD hecha en Access y la intención es que este post tenga mayor alcance.

EjemFactura

Buenas.

11 Comentarios

  1. Joel rojas Joel rojas
    Agosto 29, 2014    

    Excelente aporte amigo, si q me va a yudar un monton

    • YAcosta YAcosta
      Septiembre 6, 2016    

      De nada Joel

  2. Noviembre 15, 2014    

    Estimado Yvan,

    Hacen más de 15 años que programo en VB. Hasta la fecha nunca había podido hacer un reporte Maestro-Detalle sin hacer uso del detestable DataEnviroment.

    En algún momento de mi historia decidí dejar de usar los asistentes y controles gráficos como el ADODatacontrol o el DataEnviroment por las limitaciones que me significaban, además de la correspondiente degradación del rendimiento que sufrían las aplicaciones que hacían uso de ellos.

    Desde ese momento comenzé a diseñar mis DataReports y llamarlos en forma dinámica a través del código, pero una materia pendiente que me quedó durante todos estos años fue la de poder llamar a un reporte Maestro-Detalle directamente desde el código.

    Con el paso de los años y el venir de tecnologías mucho más estándar que nuestro viejo y querido VB6 me incliné más al desarrollo con PHP y el uso de HTML5+CSS+Javascript obteniendo excelentes resultados.

    Sin embargo, esta semana me encargaron hacer un mantenimiento a una vieja aplicación de escritorio desarrollada en VB6 contra MySQL. Parte de ese mantenimiento incluía la generación de reportes Maestro-Detalle y fue así como mi búsqueda me trajo directamente a tu sitio web, el cual desconocía completamente.

    Gracias a este Post he conseguido comprender la forma de llamar a esos dichosos reportes directamente desde el código, por lo cual te estoy completamente agradecido.

    Por ultimo comentar que nunca he usado Firebird, siempre me manejé con Access, MySQL y desde hace unos años con MSSQL Server Express. De todas formas valoro muchísimo que intentes difundir un motor Libre y Gratuito y te aliento por ello.

    Reitero mis agradecimientos por tu publicación, Felicidades!

    • YAcosta YAcosta
      Noviembre 15, 2014    

      Hola Francisco.
      Mas bien gracias a ti por tomarte el tiempo en escribir y agradecer, me alegra el dia saber que pude colaborarte. Fuerte abrazo desde Lima-Perú.

  3. Waldo Waldo
    Diciembre 18, 2014    

    Yvan, muy bueno tu articulo. Hace años que uso VB y ADO y nunca habia visto un buen ejemplo de como usar el SHAPE.
    Muchas gracias!

    Saludos

    Waldo

    • YAcosta YAcosta
      Diciembre 18, 2014    

      Gracias Waldo, estoy queriendo darme tiempo para sacar un articulo donde muestre dos subniveles jerarquicos pero ya sabes el trabajo complica las cosas, en la primera que pueda retomo esto. Saludos

  4. Fernando Fernando
    Diciembre 21, 2014    

    me gustaria me pudieras proporcionar un ejemplo en elcual se aplica lo anteriromente descrito, me concidero tener un nivel principiante en bv6

    • YAcosta YAcosta
      Diciembre 21, 2014    

      Hola Fernando, no entiendo tu petición, a parte de que todo esta detalladamente explicado allí esta adjunto el ejemplo para que lo descargues, creo que no lo viste, se llama EjemFactura. Saludos

  5. Enero 4, 2015    

    Vaya..! te has pasado con el ejemplo mi estimado YVan… la verdad muy bien detallado el uso mediante código, y a decir verdad hasta el día de hoy no entiendo como podemos ahogarnos en un vaso de agua, porque no creo que los de M$ sean tan p*lo**2 en hacer una herramienta que no se pueda utilizar (en vez de facilitar te complica el trabajo), hasta el dia de hoy vengo dando mantenimiento a sistemas hechos en vb6.0 con access, un paso a la vez vamos mejorando y retocando código y exportando datos a Firebird (que gracias a Uds. lo investigue). Me queda seguir urgando en internet del como hacer ciertas cosas…
    Y bueno, nada más que alentandolo a que desarrolle ejemplos prácticos para ir aumentando nuestra sapiencia…
    Saludos

  6. Uriel Uriel
    Agosto 23, 2016    

    Yo tenia experiencia con el shape append, pero con tu ejemplo aclare muchisimas cosas, como la de asignar el recordset al datareport.
    Gracias.

    • YAcosta YAcosta
      Septiembre 6, 2016    

      Que bueno. Saludos Uriel

No Pings Yet

  1. DataReport vs Crystal Reports - VB-MUNDO - Programacion Visual on Julio 13, 2014 at 10:15 pm
  2. Anónimo on Marzo 13, 2015 at 11:44 am

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*

Protected by WP Anti Spam

Gracias por su visita.