16 enero 2009

Creación de informes con QuickReport (III)

Hoy vamos a ver como modificar la impresión de un informe en tiempo real para poder cambiar su aspecto. También veremos otro ejemplo de un informe con detalle y subdetalle.

MODIFICANDO LA IMPRESIÓN EN TIEMPO REAL


Supongamos que en el listado de clientes que vimos en ejemplos anteriores hemos añadido la columna SALDO INICIAL y nos interesa que salgan todos los importes en negro menos aquellos con saldo negativo que aparecerán en rojo:

Cuando se insertan campos en un informe, Delphi suele darles el nombre QRDBText1, QRDBText2, etc. Debemos acostumbrarnos a poner en la propiedad Name de estos componentes el mismo nombre que tiene el campo en la tabla, en nuestro caso SALDOINICIAL. Sólo para los campos de la base de datos, no es necesario para etiquetas y figuras gráficas.

También sería recomendable ponerle nombre a la banda donde se imprimen los campos (Detalle).

Luego programamos el evento BeforePrint de la banda Detalle del siguiente modo:

procedure TFListadoClientes.DetalleBeforePrint(Sender: TQRCustomBand;
var PrintBand: Boolean);
begin
if Form1.Clientes['SALDOINICIAL'] >= 0 then
SALDOINICIAL.Font.Color := clBlack
else
SALDOINICIAL.Font.Color := clRed;
end;

El evento BeforePrint (antes de imprimir) se va a ejecutar antes de dibujar cada registro en la banda. De este modo podemos cambiar en tiempo real la visualización de cada campo antes de que se plasme en el folio.

Otro caso especial que nos puede solucionar este método es el de modificar los campos booleanos para que no imprima True o False. Por ejemplo, si quiero imprimir el campo activo pasaría esto:


Para solucionar esto en vez de imprimir directamente el campo ACTIVO utilizando un objeto TQRBDText lo que vamos a hacer es meter una etiqueta llamada EActivo (TQRLabel):


Ahora modificamos el método BeforePrint de la banda Detalle:

procedure TFListadoClientes.DetalleBeforePrint(Sender: TQRCustomBand;
var PrintBand: Boolean);
begin
if Form1.Clientes['SALDOINICIAL'] >= 0 then
SALDOINICIAL.Font.Color := clBlack
else
SALDOINICIAL.Font.Color := clRed;

if Form1.Clientes['ACTIVO'] then
EActivo.Caption := 'SI'
else
EActivo.Caption := 'NO';
end;

Si el campo ACTIVO está a True imprimimos SI, NO en canso contrario. Estos truquillos nos ahorran mucho tiempo y evitan tener que modificar los datos de la tabla original.

Hasta modemos meter en la banda figuras gráficas o iconos según los datos del registro actual. Pongamos por ejemplo que a los clientes cuyo saldo inicial sea superor a 800 euros le ponemos el icono de un lápiz a lado como advertencia para su edición:

Lo que he hecho es insertar el objeto imagen TQRImage con el icono del lápiz (a través de su propiedad Picture) y luego vuelvo a ampliar el evento BeforePrint de este modo:

procedure TFListadoClientes.DetalleBeforePrint(Sender: TQRCustomBand;
var PrintBand: Boolean);
begin
if Form1.Clientes['SALDOINICIAL'] >= 0 then
SALDOINICIAL.Font.Color := clBlack
else
SALDOINICIAL.Font.Color := clRed;

if Form1.Clientes['ACTIVO'] then
EActivo.Caption := 'SI'
else
EActivo.Caption := 'NO';

if Form1.Clientes['SALDOINICIAL'] >= 800 then
Icono.Enabled := True
else
Icono.Enabled := False;
end;

Como puede verse en el código he utilizado la propiedad Enabled en vez de la propiedad Visible para ocultar el icono. La propiedad Visible no tiene ningún efecto en QuickReport (por lo menos a mi no me ha funcionado en ningún caso).

Con estos pequeños retoques nuestros informes parecerán mucho más profesionales (y a nuestros clientes les sacamos más pasta diciendo que nos ha costado mucho hacerlo :)

CREANDO UN INFORME CON DETALLE Y SUBDETALLE

Ahora vamos a complicar un poco más el asunto y vamos hacer un listado desglosado de facturas con dos bandas de tipo detalle. Más bien con un detalle y un subdetalle:


Este listado muestra las cabeceras de las facturas (los totales) y sus líneas de detalle. Este sería su diseño en QuickReport:


El documento se compone de tres bandas:

Cabecera: esta banda es un objeto TRQBand y va a ser de tipo rbTitle. En esta banda sólo vamos a meter el título del listado:


Detalle: también va a ser un objeto TRQBand y su tipo será rbDetail. En esta banda necesitamos los títulos de la factura así como los campos de la misma:


El único campo que no hay que vincular con la tabla Factura es el NOMBRE que va vinculado a la tabla CLIENTES.

SubDetalle: aquí introducimos una banda de tipo TQRSubDetail. No es lo mismo una banda TRQBand de tipo rbSubDetail que una banda TQRSubDetail. La primera sólo suele utilizarse para totalizar datos (sin vincularlo a tablas) pero esta última tiene la propiedad DataSet que permite vincular sus campos a otra tabla:


Ahora viene la vinculación con las tablas. Primero hay que seleccionar el objeto TQuickRep y vincular a su propiedad DataSet la tabla Facturas (la cabecera). En la banda de Detalle vinculamos todos los campos a la tabla Facturas.

En la banda SubDetail tenemos que vincular los campos a la tabla Detalle (el contenido de la factura).

Si ejecutáramos el listado como está, nos pasaría esto:


Han ocurrido dos cosas:

1º Al no filtrar la tabla de Clientes por cada factura resulta que aparece el mismo cliente en ambas facturas. Debería ser: CLIENTES.ID = FACTURAS.IDCLIENTE.

2º Al no filtrar la tabla Detalle por cada factura nos aparece el detalle de ambas facturas debajo de cada cabecera. Debería ser así: DETALLE.IDFACTURA = FACTURAS.ID.

Para solucionar esto tenemos que escribir este código en el evento BeforePrint (antes de imprimir) de la banda Detalle:

procedure TFListadoFacturas.DetalleBeforePrint(Sender: TQRCustomBand;
var PrintBand: Boolean);
begin
with Form1 do
begin
Clientes.Filter := 'ID=' + IntToStr(Facturas['IDCLIENTE']);
Clientes.Filtered := True;

Detalle.Filter := 'IDFACTURA=' + IntToStr(Facturas['ID']);
Detalle.Filtered := True;
end;
end;

Lo que hacemos es filtrar el cliente y el detalle de la factura respecto a la factura que estamos imprimiendo en cada momento. De este modo no hay que recurrir a sentencias SQL para filtrar la tablas (aunque también se podría hacer así).

En la siguiente parte de este artículo veremos más ejemplos de informes así como la forma de exportar a PDF y otros formatos.

Pruebas realizadas en RAD Studio 2007.

09 enero 2009

Creación de informes con QuickReport (II)

Después de crear nuestro primer informe con QuickReport vamos a hacer otro a dos bandas el cual va a ser un listado de clientes. En un listado cualquiera necesitamos que la banda superior se mantenga fija en todas las hojas que se impriman y que la banda que va a imprimir lo datos se adapte al folio según los mismos.

CREANDO UN LISTADO SIMPLE A DOS BANDAS

Para cumplir nuestro cometido vamos a crear un nuevo formulario llamado FListadoClientes y dentro del mismo volvemos a añadir el objeto de la clase TQuickRep que llamaremos Informe.

Como necesito imprimir columnas bien anchas entonces debemos imprimir el folio de manera apaisado. Esto se hace pinchando el objeto QuickRep con el botón derecho del ratón y seleccionando Report Settings:


Y aparecerá este cuadro de diálogo:


Como puede verse en la imagen no sólo podemos seleccionar el tipo de folio (A4, A5, B4, etc.) sino que además se puede configurar el folio a un ancho y alto determinado en milímetros que viene muy bien para formatos de documentos que se imprimen en impresoras matriciales con papel continuo (lo más asqueroso para un programador).

En la parte derecha de esta ventana tenemos un ComboBox que por defecto tiene seleccionada la opción Portrait. Esto significa que va a imprimir el folio verticalmente. Como lo vamos a imprimir apaisado seleccionamos LandScape.

Ahora añadimos al objeto TQuickRep un par de bandas (TQRBand) y a la segunda banda le podemos mediante el inspector de objetos a su propiedad BandType el valor rbDetail:


También es buena costumbre ponerle nombre a las bandas para luego poder modificar sus acciones mediante código. A la primera banda le ponemos en su propiedad Name el nombre Cabecera y a la segunda banda la llamamos Detalle.

Insertamos etiquetas en la cabecera mediante objetos TQRLabel y los campos en el detalle mediante TQRDBText:


Al igual que hicimos en el artículo anterior debemos vincular este formulario a la unidad donde están los dados (uses Unit1) y añadir en la propiedad DataSet del objeto TQuickReport la tabla Clientes.

Hay que procurar que la banda de Detalle tenga la altura ajustada a los campos a imprimir, para que la separación de las líneas no sea muy grande y no se desperdicien folios (algunos jefes de empresas son muy tacaños).

Creamos el formulario del informe y lo ejecutamos desde la ventana principal:

Application.CreateForm( TFListadoClientes, FListadoClientes );
FListadoClientes.Informe.Preview;

Al ejecutar nuestro informe ya lo tendremos apaisado:


INFORMES MAESTRO/DETALLE A TRES BANDAS

El siguiente ejemplo que vamos a ver es la impresión de una factura que tiene cabecera, detalle y pie así como los datos de un cliente que viene de otra tabla, es decir, vamos a imprimir datos de tres tablas a la vez.

Como en los casos anteriores creamos un nuevo formulario llamado FImprimirFactura e insertamos el objeto TQuickRep y lo llamamos Informe.

Para este ejemplo voy a añadir al formulario principal un nuevo objeto TClientDataSet llamado Facturas con los siguientes campos:


Esta sería la cabecera de la factura. Ahora creamos otro ClientDataSet para el Detalle de la factura con estos campos:


Introducimos una factura en la tabla con un par de líneas de detalle:

Facturas.Append;
Facturas['ID'] := 1;
Facturas['FECHA'] := Date;
Facturas['IDCLIENTE'] := 1;
Facturas['BASEIMPONIBLE'] := 300;
Facturas['IVA'] := 48;
Facturas['TOTAL'] := 348;
Facturas.Post;

Detalle.Append;
Detalle['IDFACTURA'] := 1;
Detalle['UNIDADES'] := 1;
Detalle['ARTICULO'] := 'MOVIL SAMSUNG';
Detalle['PRECIO'] := 100;
Detalle['TOTALLINEA'] := 100;
Detalle.Post;

Detalle.Append;
Detalle['IDFACTURA'] := 1;
Detalle['UNIDADES'] := 1;
Detalle['ARTICULO'] := 'MOVIL NOKIA';
Detalle['PRECIO'] := 200;
Detalle['TOTALLINEA'] := 200;
Detalle.Post;

A continuación creamos un nuevo formulario llamado FImprimirFactura e insertamos un objeto TQuickRep con tres bandas:


A las bandas las vamos a llamar Cabecera, Detalle, Pie y serán del tipo (BandType) rbTitle, rbDetail y rbPageFooter respectivamente.

Como hicimos en ejemplos anteriores, vinculamos a formulario con el formulario principal para poder traernos los datos de las tablas (uses Unit1). Seleccionamos el objeto TQuickRep y vinculamos mediante la propiedad DataSet la tabla Detalle.

Aunque pueda parecer extraño, cuando vamos a imprimir un documento maestro/detalle debemos vincular a la propiedad DataSet del objeto TQuickRep la tabla Detalle y no la del maestro. Esto se debe a que es el detalle la banda que se va a repetir en la factura, es decir, el centro de la misma. La cabecera y el pie son sólo datos adicionales. Eso no quita que luego cada campo se vincule con una tabla distinta. No hay limitaciones a la hora de vincular campos con tablas.

Pero vayamos por partes. Primero implementamos la cabecera de la factura en la primera banda:


Los campos ID y FECHA están vinculados con la tabla FACTURAS. En cambio todos los campos encerrados en el rectángulo están vinculados con la tabla CLIENTES (sólo hay que procurar tener filtrado que FACTURA.IDCLIENTE = CLIENTES.ID).

Lo demás son todo etiquetas TQRLabel. El rectángulo y las líneas que he puesto debajo de los títulos de los campos del detalle lo he hecho con un objeto TQRShape que en su propiedad Shape soporta estos formatos:

qrsRectangle: Un rectángulo.
qrsCircle: Un círculo.
qrsHorLine: Línea horizontal.
qrsVerLine: Línea vertical.
qrsRoundRect: Rectángulo con las esquinas redondeadas.
qrsRightAndLeft: dos líneas verticales (un rectángulo sin techo ni suelo).
qrsTopAndBottom: dos líneas horizontales (un rectángulo sin paredes).

En mi formato de factura sólo he utilizado líneas horizontales y un rectángulo.

Vamos ahora con el detalle de la factura en la segunda banda:


Esta banda sólo va a contener campos TQRDBText vinculados a la tabla DETALLE. Es importante quitar en este tipo de campos el valor Autosize y ajustar el ancho manualmente para que por ejemplo el nombre del artículo no chafe el precio en el caso de que el nombre sea demasiado largo.

También es importante que todos los campos de tipo float estén alineados a la derecha mediante su propiedad Alingment = taRightJustify.

Hay que procurar que la altura de la banda de detalle sea un poco más grande que la altura de los campos para que el espacio entre líneas no sea ni muy ancho ni muy estrecho.

Vamos con la última banda, el pie:


Esta banda sólo contiene los tres campos que totalizan la factura y que están vinculados con la tabla FACTURAS. Un detalle que hay que tener presente es que la posición vertical a la que van a salir estos totales es inversamente proporcional a la altura de la banda de tipo rbPageFooter, es decir, si queremos que los totales salgan más arriba hay que ampliar verticalmente la altura de esta banda. Y si queremos que los totales salgan pegados en la parte inferior del folio entonces hay que procurar que esta banda sea lo más estrecha posible (verticalmente).

Este seria nuestro diseño de la factura:



Llamamos a la vista previa como siempre:

Application.CreateForm( TFImprimirFactura, FImprimirFactura );
FImprimirFactura.Informe.Preview;

Y esta sería la vista previa al imprimirla:


Esta sería la parte superior:


Y la parte inferior:


También sería recomendable que los campos que vamos a imprimir de tipo moneda tengan definida su máscara a dos decimales para evitar problemas de redondeo. Esto se hace en el diseño de la tabla original a través de la propiedad DisplayFormat:


De modo que al imprimir aparezcan los dos decimales:

Estos tres ejemplos que hemos visto son los tres típicos informes que nos suelen pedir: un formulario, un listado o un documento maestro/detalle (y pie). Aunque aquí no se acaban nuestras pesadillas ya que esto solo son juguetes comparado con lo que los clientes suelen pedir de verdad.

En el próximo artículo veremos casos más complejos y algunos truquillos para modificar el aspecto del informe en tiempo real.

Pruebas realizadas en RAD Studio 2007.

02 enero 2009

Creación de informes con QuickReport (I)

Aunque en los tiempos que estamos disponemos de gran cantidad de generadores de informes para Delphi (Rave Reports, Report Maganer, FastReport, etc.) hay que reconocer que QuickReport es el que tiene mayor predilección por parte de los usuarios debido a que venía incorporado de serie el las primeras versiones de Delphi.

Luego Borland-Inprise-CodeGear se deshizo de él a partir de Delphi 7 (no se porque) e incorporó Rave Reports, donde este último destacaba por tener su propio editor, por tener una documentación cutre e inexistente así como por dar Access Violations si lo mirabas mal.

Pero la empresa QBS Software siguió ampliando estos componentes hasta incluso las últimas versiones de Delphi 2009.

Los componentes QuickReport son comerciales estando actualmente en la versión 5.0 (podemos descargar una versión trial para probarlos). Para la serie de artículos que voy a escribir es suficiente con tener la versión 4.xx que está disponible desde para las versiones de Delphi 5, 6 y 7.

Para escribir este artículo me he basado en la versión 4.07 de QuickReport para Delphi 2007 instalándolo en RAD Studio 2007.

INSTALANDO LOS COMPONENTES QUICKREPORT

Aunque las instalaciones suelen variar en aspecto de una versión a otra, la forma de instalarlas suele ser la misma:

1º Descargamos el archivo de la instalación en un exe o archivo zip y al ejecutarla se instalará en un subdirectorio según la ruta donde tengamos instalado Delphi o RAD Studio:


2º Cuando finalice la instalación abrimos nuestra versión de Delphi habitual y abrimos los archivos con extensión DPK para instalarlos:


3º Una vez instalados los componentes deben aparecer en la paleta de herramientas:


CREANDO UN NUEVO PROYECTO

Al crear un nuevo proyecto debemos configurar en las opciones de nuestro proyecto la ruta donde se encuentran los componentes QuickReport. En Delphi 2007 se haría seleccionando Project -> Options y en el apartado Directories/Conditionals introducimos la ruta donde se han instalado los componentes, por ejemplo:


De este modo ya no dará problemas al compilar.

CREANDO LA BASE DE DATOS PARA IMPRIMIR EL INFORME

A modo de ejemplo vamos a crear una tabla de memoria utilizando los componentes de la clase TClientDataSet para crear una base de datos de clientes para luego imprimir su formulario en QuickReport.

De la pestaña Data Access extraemos el componente ClientDataSet y los insertamos en el formulario con el nombre Clientes:


En la propiedad FieldDefs de este componente vamos a definir los siguientes campos:


Para convertirlo en una tabla de memoria pinchamos el componente con el botón derecho del ratón y seleccionamos Create Dataset:


También voy a añadir un botón cuyo evento sea añadir un registro a la tabla:

procedure TForm1.BIntroducirDatosClick(Sender: TObject);
begin
Clientes.Append;
Clientes['ID'] := 1;
Clientes['NOMBRE'] := 'PABLO AGUILAR RUIZ';
Clientes['CIF'] := '11222333Z';
Clientes['DIRECCION'] := 'PLAZA ESPAÑA, 8';
Clientes['POBLACION'] := 'MADRID';
Clientes['CP'] := '28547';
Clientes['PROVINCIA'] := 'MADRID';
Clientes['SALDOINICIAL'] := 4200.28;
Clientes['FECHAALTA'] := '12/05/2008';
Clientes['ACTIVO'] := False;
Clientes.Post;
end;

Ahora pasemos a imprimir un informe con estos datos.

CREANDO EL INFORME

Un informe creado en QuickReport va vinculado directamente a un formulario de Delphi por lo que obligatoriamente debemos utilizar como mínimo un formulario por cada informe. También tiene sus ventajas y es que aprovecha las nuevas características del editor de formularios de los nuevos Delphi.

Creamos un nuevo formulario que vamos a llamar FCliente y dentro del mismo añadimos el componente TQuickRept:


Hay que ampliar bien el formulario ya que nos inserta el equivalente a un folio A4 donde vamos a diseñar nuestro informe. Encima de este componente es donde debemos añadir sólo componentes de la pestaña QuickReport (no vale meter por ejemplo un componente TLabel, si lo deja pero no imprimirá nada).

Lo siguiente que tenemos que hacer es insertar una banda la cual va a contener los datos que queremos imprimir. Esto se hace añadiendo el componente de la clase TQRBand dentro del informe:


La banda que hemos insertado es de tipo Title, es decir, sólo se va a imprimir una sola vez independientemente del número de registros tenga nuestra tabla. Esto puede verse en el inspector de objetos (cuando tenemos la banda seleccionada) con su propiedad BandType:


Como vamos a imprimir la ficha de un cliente podemos dejarla como esta y empezar a meter componentes. Comenzamos añadiendo etiquetas para los nombres de los campos utilizando el componente TQRLabel:


Para poder añadir los campos de la base de datos de clientes debemos añadir a este formulario la unidad donde se halla la tabla que queremos imprimir (en mi ejemplo Form1):

uses Unit1;

Ahora pinchamos sobre cualquier borde del objeto TQuickRep y en el inspector de objetos le ponemos el su propiedad DataSet la tabla que queremos imprimir:


Una vez hecho esto ya podemos introducir campos de base de datos para traernos la información. Una de las ventajas de las que goza QuickReport es que podemos vincular directamente el informe a una tabla de datos sin tener que utilizar el objeto DataSource de la pestaña DataAccess.

Para introducir los campos vamos a utilizar el componente TQRDBText:


Sólo hay que procurar que cada campo tenga las propiedades DataSet (Form1.Clientes) y DataSource (el nombre del campo) rellenas.

EJECUTANDO LA VISTA PREVIA DEL INFORME

Desde nuestro formulario principal (Form1) voy a crear un botón para abrir la vista previa del informe que hemos hecho. Antes de esto tengo que vincular la unidad UClientes.pas a nuestro formulario para poder llamarlo.

Para programar de una manera más clara voy a llamar al objeto TQuickRep con el nombre Informe. Así se llama a la vista previa:

procedure TForm1.BInformeClientesClick(Sender: TObject);
begin
FCliente.Informe.Preview;
end;

Con algo tan simple como esto ya tenemos el informe en pantalla:


En principio nos aparece la vista previa del informe en una ventana pequeña en el centro de la pantalla. En la parte izquierda de la ventana nos aparecen dos pestañas:

Thumbails: contiene la vista previa cada folio que se imprime (al estilo PowerPoint).

Search Result: permite hacer búsquedas de texto en las hojas impresas.

Si nos interesa que se vea sólo el informe a pantalla completa entonces seleccionamos el objeto TQuickRep llamado Informe y en su propiedad PreviewInitialState seleccionamos wsMaximized.

Para quitar las pestañas Thumbails y Search result hay que deshabilitar las opciones PrevShowThumbs y PrevShowSearch. Y por último vamos a ampliar la visualización del folio (el Zoom) seleccionando en su propiedad PrevInitialZoom el valor qrZoomToWith. Este sería el resultado:


También sería conveniente darle un título al informe sobre todo porque si lo mandamos a imprimir aparecerá sin título en la cola de impresión. Para ello escribimos en su propiedad ReportTitle por ejemplo Datos del cliente. Puede verse al abrir la vista previa:


Esta barra superior contiene los típicos botones para modificar el zoom, avanzar o retroceder por las páginas, modificar las propiedades de la impresión o imprimir directamente. QuickReport también permite guardar el informe generado en un archivo con extensión QRP para poder abrirlo posteriormente y reimprimirlo.

No hay que confundir esto con una plantilla de informe ya que lo que guardamos el todo el informe (incluyendo los datos). Esto puede ser útil para guardar copias de factura, recibos, etc. cuando los datos ya no existen en nuestra base de datos y necesitamos reimprimirlos por pérdida del documento original. También nos evita volver a procesar listados lentos (informe de estadísticas de enero, febrero, etc.). Con procesarlo una vez sobra.

Esta sería la parte básica de generación de informes aunque veremos en artículos posteriores que QuickReport es mucho más potente que esto.

Pruebas realizadas en RAD Studio 2007.

19 diciembre 2008

Generando un informe de Microsoft Word

Aunque hay muchos generadores de informes para Delphi como pueden ser QuickReport, Rave Reports, Report Manager, etc., no hay procesador de textos más completo que Microsoft Word.

Es este artículo vamos a ver como generar un nuevo documento de Word y poder introducir textos, tablas, figuras, notas al pie, etc. Esto lo vamos a conseguir utilizando el componente TWordApplication que encuentra en el apartado Servers dentro de la paleta de componentes.

Aunque no he conseguido averiguar la inmensidad de funciones que incorpora este objeto sí que podemos generar un informe más o menos decente.

EL COMPONENTE TWORDAPPLICATION

Insertamos el componente en nuestro formulario y lo llamamos Word para abreviar:


Este es el documento que vamos a generar:


Creamos un par de variables del tipo OleVariant para poder enviar parámetros a funciones:

var
Documento, Texto: OleVariant;

Conectamos con Microsoft Word y le decimos que cree un nuevo documento que va a llamarse Informe.doc y que va a guardarse en el mismo directorio donde nos encontramos:

// Conectamos con Word y creamos un nuevo documento
Documento := ExtractFilePath( Application.ExeName ) + 'Informe.doc';
Word.Connect;
Word.Documents.Add(EmptyParam,EmptyParam,EmptyParam,EmptyParam);

Entramos al encabezado del documento y escribimos el título centrado:

with Word do
begin
with Selection do
begin
// Insertamos un título en el encabezado del documento
with Sections.Item(1).Headers.Item(1).Range do
begin
Font.Name := 'Courier New';
Font.Bold := 1;
Text := 'TITULO DEL DOCUMENTO';
Paragraphs.Item(1).Alignment := 1; // centramos párrafo
end;

La propiedad Selection contempla el punto donde se encuentra el cursor al principio de la página.

Dentro del documento escribimos tres frases de un color distinto:

// Establecemos la fuente Tahoma, negrita, azul y tamaño 10
Font.Name := 'Tahoma';
Font.Size := 10;
Font.Color := clNavy;
Font.Bold := 1;
TypeText( 'Frase 1' + #13 );
Font.Color := clRed;
TypeText( 'Frase 2' + #13 );
Font.Color := clGreen;
TypeText( 'Frase 3' + #13 + #13 + #13 );

También vamos a insertar una nota al pie de página y otra al final del documento:

// Insertamos una nota al pie y una nota al final
Texto := 'Nota al pie de página';
FootNotes.Add( Range, EmptyParam, Texto );
Texto := 'Nota al final';
EndNotes.Add( Range, EmptyParam, Texto );

Además insertamos una tabla configurando todas las celdas:

// Insertamos una tabla con fuente de color negro
Font.Color := clBlack;
with Tables.Add( Range, 3, 4, EmptyParam, EmptyParam ) do
begin
// Títulos de la tabla
Cell(1,1).Range.Text := 'Unidades';
Cell(1,2).Range.Text := 'Artículo';
Cell(1,3).Range.Text := 'Precio';
Cell(1,4).Range.Text := 'Total';
Rows.Item(1).Shading.BackgroundPatternColor := clGreen; // fonfo verde
Rows.Item(1).Range.Font.Color := clYellow; // fuente amarilla

// Contenido
Cell(2,1).Range.Text := '1';
Cell(2,2).Range.Text := 'PORTATIL ACER';
Cell(2,3).Range.Text := '540';
Cell(2,4).Range.Text := '540';
Cell(3,1).Range.Text := '2';
Cell(3,2).Range.Text := 'RATON OPTICO';
Cell(3,3).Range.Text := '2,50';
Cell(3,4).Range.Text := '5';
Columns.Item(4).Shading.BackgroundPatternColor := clBlue; // fondo azul
end;
end;
end;

Para crear una nueva tabla hemos llamado a:

Tables.Add( Range, 3, 4, EmptyParam, EmptyParam )

El segundo parámetro determina el número de filas y el tercero el número de columnas.

Aparte de añadir el contenido a cada celda hemos establecido para toda la primera fila el fondo de color verde y la fuente de color amarillo:

Rows.Item(1).Shading.BackgroundPatternColor := clGreen; // fonfo verde
Rows.Item(1).Range.Font.Color := clYellow; // fuente amarilla

Igualmente le hemos dicho a la cuarta columna que el color de fondo es azul:

Columns.Item(4).Shading.BackgroundPatternColor := clBlue; // fondo azul

Aun así, si queremos ser más específicos podemos hacerlo para cada celda (Cells).

Por último he añadido una línea recta cuyas coordenadas van por pixels:

Word.ActiveDocument.Shapes.AddLine( 200, 200, 250, 250, EmptyParam );

Una vez hemos finalizado el documento lo guardamos y desconectamos de Word:

Word.ActiveDocument.SaveAs( Documento,
EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam );
Word.Quit;
Word.Disconnect;

Aunque me hubiera gustado poder añadir muchos más elementos a este informe, debido a la pésima documentación de Delphi 2007 y que en la web tampoco abundan muchos ejemplos (lo único decente que he visto es de unas páginas rusas y japonesas). Si averiguo más cosas lo ampliaré en un futuro.

Pruebas realizadas en RAD Studio 2007.

05 diciembre 2008

Creando una hoja de cálculo desde Delphi

Al igual que en el artículo anterior vimos como leer datos de una hoja de cálculo de Microsoft Excel hoy vamos a ver como crearla, rellenarla con datos y fórmulas para luego guardarla en disco.

Para ello utilizaremos el mismo componente: TExcelApplication. Le pondremos igualmente en su propiedad Name el nombre Excel. Mi objetivo es crear esta hoja de cálculo:


Esta hoja contiene 4 columnas con las unidades, el nombre del artículo, el precio y total línea. La columna del total debe contener una fórmula que multiplique las unidades por el precio.

Vamos a ver paso a paso como crear esta hoja de cálculo:

1º Declaramos la variable Hoja de tipo _WorkSheet para que apunte a la hoja de cálculo con la que estamos trabajando:

var
Hoja: _WorkSheet;

2º Ejecutamos una instancia de Excel y creamos un nuevo libro (Workbook):

// Abrimos excel
Excel.Connect;

// Creamos un nuevo libro con tres hojas (predeterminado)
Excel.Workbooks.Add( NULL, 0 );

3º A la primera hoja del libro la llamamos Presupuesto:

// Apuntamos a la primera hoja y le cambiamos el nombre
Hoja := Excel.Worksheets.Item[1] as _WorkSheet;
Hoja.Name := 'Presupuesto';

4º Creamos los títulos de las columnas:

// Títulos de los datos
Hoja.Range['A1','A1'].Value2 := 'UNIDADES';
Hoja.Range['B1','B1'].Value2 := 'ARTICULO';
Hoja.Range['B1','B1'].ColumnWidth := 30;
Hoja.Range['C1','C1'].Value2 := 'PRECIO';
Hoja.Range['D1','D1'].Value2 := 'TOTAL';
Hoja.Range['A1','D1'].Font.Bold := True;

Aparte de introducir los títulos he utilizado las propiedades ColumnWidth y Font.Bold para ensanchar la columna del artículo y para poner todos los títulos en negrita.

5º Introducimos los datos dentro de las columnas:

// Datos
Hoja.Range['A2','A2'].Value2 := 3;
Hoja.Range['B2','B2'].Value2 := 'BOLIGRAFOS';
Hoja.Range['C2','C2'].Value2 := 1.25;
Hoja.Range['A3','A3'].Value2 := 2;
Hoja.Range['B3','B3'].Value2 := 'LIBRETAS';
Hoja.Range['C3','C3'].Value2 := 2.4;
Hoja.Range['A4','A4'].Value2 := 5;
Hoja.Range['B4','B4'].Value2 := 'LAPICES';
Hoja.Range['C4','C4'].Value2 := 0.45;

Al ser la propiedad Value2 de tipo Variant podemos introducir datos de todo tipo sin necesidad de realizar conversiones.

6º En la última columna vamos a introducir una fórmula para multiplicar unidades por precio:

// Fórmulas
Hoja.Range['D2','D2'].Formula := '=A2*C2';
Hoja.Range['D3','D3'].Formula := '=A3*C3';
Hoja.Range['D4','D4'].Formula := '=A4*C4';

7º Damos formato decimal a las columnas del precio y los totales y para ésta última columna le cambiamos los colores:

// Formato decimal
Hoja.Range['C2','D4'].NumberFormat := '0,00';

// Damos formato a los totales
Hoja.Range['D2','D4'].Font.Bold := True; // fuente negrita
Hoja.Range['D2','D4'].Font.Color := clBlue; // fuente azul
Hoja.Range['D2','D4'].Borders.Color := clRed; // borde rojo
Hoja.Range['D2','D4'].Interior.Color := clYellow; // fondo amarrillo

8º Por último guardamos la hoja de cálculo y desconectamos de Excel:

// Lo primero que hacemos es guardarlo
Excel.ActiveWorkbook.SaveAs( ExtractFilePath( Application.ExeName ) + 'Nueva.xls',
EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, xlNoChange,
EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, 0);

Excel.Quit;
Excel.Disconnect;

La hoja de cálculo la hemos guardado con el nombre Nueva.xls dentro de mismo directorio donde se ejecuta nuestra aplicación.

Y es que no hay nada mejor que sacarle partido a Microsoft Office para que nos haga el trabajo sucio.

Pruebas realizadas en RAD Studio 2007.

28 noviembre 2008

Leyendo datos de hojas de cálculo de Microsoft Excel

Si hay algo que nos piden frecuentemente a los programadores de gestión es poder recoger o enviar datos a los programas ofimáticos más populares: Microsoft Word, Microsoft Excel y Microsoft Access.

Lo más común suele ser leer datos de una hoja de cálculo y guardarlos en nuestra base de datos, aunque también tenemos la posibilidad de enviarle la información para que Excel nos haga una gráfica espectacular.

Una hoja de cálculo es realmente un libro que puede contener una o más hojas de cálculo:


A su vez, Microsoft Excel puede abrir varios libros a la vez.

EL COMPONENTE TEXCELAPPLICATION

En la paleta de componentes tenemos el componente de la clase TExcelApplication que se encuentra en la sección Servers:


Lo insertamos en el formulario donde vamos a hacer la importación y lo llamamos Excel para simplificar:


Supongamos que queremos leer esta hoja de cálculo:


Primero abrimos la hora de cálculo:

Excel.Workbooks.Open( ExtractFilePath( Application.ExeName ) + 'Hoja.xls',
EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam, EmptyParam, 0 );

Hay que reconocer que la cantidad de parámetros que tiene el método Open es impresionante:


Aunque no hay que asustarse porque lo que necesitamos realmente es el primer parámetro (el nombre del archivo excel a abrir). Los demás son para elegir si queremos abrirlo en modo sólo lectura, introducir una contraseña, etc.

Una vez que hemos abierto la hora de cálculo tenemos que situarnos en la Hoja1. Para ello creamos las siguientes variables:

var
i: Integer;
si: String;
Hoja: _WorkSheet;

Las variables i y si son para recorrer las celdas de la hoja de cálculo y la variable Hoja va a apuntar a una hoja en concreto. En nuestro caso la primera:

Hoja := Excel.Worksheets.Item[1] as _WorkSheet;

Para leer información de las celdas de la hoja de cálculo tenemos que conocer las coordenadas horizontales (A,B,C,…) y las verticales (1,2,3,…). Cuando lea la información la voy a volcar a un componente ListView configurado con estas columnas:


Ahora sólo queda ir recorriendo cada fila hasta que encontremos una fila vacía:

i := 2;
si := IntToStr( i );
repeat
with ListView.Items.Add do
begin
SubItems.Add( Hoja.Range['A'+si,'A'+si].Value2 ); // Código
SubItems.Add( Hoja.Range['B'+si,'B'+si].Value2 ); // Nombre
SubItems.Add( Hoja.Range['C'+si,'C'+si].Value2 ); // CIF
SubItems.Add( Hoja.Range['D'+si,'D'+si].Value2 ); // Saldo
end;

Inc( i );
si := IntToStr( i );
until ( VarType( Excel.Range['A'+si,'A'+si].Value2 ) = VarEmpty );

La propiedad Value2 de cada celda devuelve un tipo Variant, por ello utilizo la función VarType para comprobar si lo que hay en la celda esta vacío (VarEmpty). De ese modo sabemos cuando hemos terminado de leer datos.

Por último cerramos la hoja de cálculo con:

Excel.Workbooks.Close( 0 );

Con esto ya hemos conseguido traernos la información de la hoja de cálculo:


Igualmente podemos escribir datos en cualquier celda de la hoja de cálculo con total naturalidad:

Hoja.Range['B2','B2'].Value2 := ‘TRANSPORTES GARCIA, S.L.’;

De esta manera podemos crear nuestras plantillas de hojas de cálculo con gráficas incluidas y desde Delphi le enviamos la información.

Pruebas realizadas en RAD Studio 2007.

Publicidad