Ir al contenido


Foto

[RESUELTO] ¿Como hacer este procedimiento?


  • Por favor identifícate para responder
18 respuestas en este tema

#1 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 29 diciembre 2009 - 08:56

Saludos.

Me disculpan por el titulo, si alguno de los moderadores le pueden encontrar mejor titulo, pues bienvenido sea es que ando medio encabretao con este problema.

El asunto comienza en que estoy utilizando Report Builder, el mismo da la posibilidad de guardar el diseño en un archivo (rtm) o en la Base de Datos con esto se gana el no tener que mandar el ejecutable cuando se modifique un reporte sino el archivo o en su defecto la sentencia SQL, que es por la cual he optado (guardar el diseño en la BD).

Decidí poner el componente de Reporte en un DataModule, con esto centralizo lo relacionado al funcionamiento de impresión; Report Builder al igual que QuickReport al momento de poner un componente (por ejemplo un Label) en la plantilla del reporte se puede acceder directamente al componente y asignar propiedades y demás.

Me refiero a que por ejemplo, ponemos un Label para desplegar el rango de fecha seleccionado por el usuario:


delphi
  1. lblFecha.Caption := 'Desde: ' + DateTimePicker1.Text + ' Hasta: ' + DateTimePicker2.Text;



Lo anterior es un ejemplo y no necesariamente debe compilar es solo para ilustrar, entonces cuando se utiliza esta modalidad de ReportBuilder primeramente se debe de cargar el diseño y en ese momento es que puedes saber cuales componentes tiene dicho diseño y poder modificar las propiedades si es necesario.

Entonces, ¿Dónde reside mi problema? (al fin) en que quiero realizar un método donde yo pueda modificar esos componentes, pero indicándole cual es el nombre del componente a modificar y que si no es invocado no realice nada:


delphi
  1. procedure TDmCustomReport.Imprimir;
  2. begin
  3.   Report.Template.DatabaseSettings.Name := AName;
  4.   Report.Template.LoadFromDatabase;
  5.   ModificarComponentes;
  6.   Report.Print;
  7. end;



ModificarComponentes sería el método que siempre se invocaría y este sería el procedimiento de como indicar:


delphi
  1. procedure TFrmReport.btnImprimirClick(Sender: TObject);
  2. begin
  3.   DmCustomReport.Modificar('lblFecha', 'MiValorAMostrar');
  4. end;



Ya con Modificar indico cual es el componente y su valor; algo así es que tengo en mente, denme una mano o pies si quiere pero algo...

Gracias anticipadas.
  • 0

#2 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.256 mensajes
  • LocationMéxico

Escrito 29 diciembre 2009 - 09:10

Hola

Desconozco como se maneja en ReportBuilder, pero en FastReport se hace algo como esto:



delphi
  1. procedure TForm1.frxReport1BeforePrint(Sender: TfrxReportComponent);
  2. begin
  3.   if Sender.Name = 'Picture1' then
  4.     tfrxpictureview(sender).picture.loadfromfile('C:\Desarrollo\fastreport\imagenes\logo.bmp');
  5. end;



Lo que hace es buscar un componente (desde el evento BeforePrint del Reporte) por su nombre en este caso es un tPicture y se carga la imagen que se requiere, espero que te de al menos una idea de como hacerlo.

Salud OS
  • 0

#3 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 29 diciembre 2009 - 09:44

Saludos.

Eso también se puede hacer en ReportBuilder, eso que hiciste es "tirar" un componente de reporte en el Form, diseñar el reporte y modificar el componente(s) con previo conocimiento del mismo.

A lo que me refiero es que, tienes el componente del reporteador en un solo sitio, sabes que tienes diseños y componentes que debes de modificar, entonces procedes a cargar el diseño por código y le indicas cuales componentes debes de modificar y con que valor.

Imagínate que tienes un solo componente en un DataModule para realizar centralizar el mayor porcentaje de rutinas para manejar la impresión.
  • 0

#4 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.256 mensajes
  • LocationMéxico

Escrito 29 diciembre 2009 - 10:19

Dejame ver si estoy entendiendo, ¿ lo que tu quieres es crear componentes en tu reporte en tiempo de ejecución ?

Salud OS
  • 0

#5 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.994 mensajes
  • LocationMadrid - España

Escrito 30 diciembre 2009 - 05:07

Entonces, ¿Dónde reside mi problema? (al fin) en que quiero realizar un método donde yo pueda modificar esos componentes, pero indicándole cual es el nombre del componente a modificar y que si no es invocado no realice nada


¿No deberías buscar los componentes con FindComponent, o enumerarlos con la propiedad Controls que tienen los formularios? De esta forma puedes filtrar los componentes sobre los que trabajarás y los que no.

Saludos.


  • 0

#6 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 30 diciembre 2009 - 07:31

Saludos.

Trate de explicarme mejor, al parecer los enrede un poco.

Primero imaginen que todos los diseños de los reportes de la aplicación están guardados en un repositorio, donde son llamados dependiendo de la opción seleccionada por el usuario.

El componente reporteador es único y esta situado en un DataModule, donde residirá la mayor parte del código con respecto a la generación de reportes. Este sería el código de carga del reporte:



delphi
  1. procedure TDmCustomReport.Imprimir;
  2. begin
  3.   Report.Template.DatabaseSettings.Name := ANombreReporte;
  4.   Report.Template.LoadFromDatabase;
  5. end;



Después de la llamada al método LoadFromDatabase es que se conocen cuales son los componentes que pertenecen al diseño seleccionado. Supongamos que el usuario ha seleccionado la opción de reporte llamada Estado de Cuenta donde se pide un rango de Fecha, esas fechas seleccionadas son las que quiero mostrar en la impresión; entonces debe existir un procedimiento donde yo le diga esto:


delphi
  1. Uses DmCustomReport;
  2.  
  3. TFrmEstadoCuenta.btnImprimir(Sender : TObject);
  4. begin
  5.   DmCustomReport.Modificar(ComponenteFecha, ValorAMostrar);
  6. end;



Como bien indica Escafandra el procedimiento que debe de existir en el DataModule donde reside el componente de Reporte debe de tener un FindComponent o cualquier otra forma para conocer los componentes que se van a modificar en Run-Time.

Ahora bien, el usuario selecciona la opción Reportes de Cliente aquí solo es necesario un Clic en el botón impresión sin mandar ningún cambio al reporte.  Sin embargo, pienso que el procedimiento de modificar debe de ejecutarse aunque no tenga nada que modificar.

Espero haber sido más claro.
  • 0

#7 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.994 mensajes
  • LocationMadrid - España

Escrito 30 diciembre 2009 - 10:53

Quizás sea por mi desconocimiento de ReportBuilder y de lo que se guarda en lo que llamas el repositorio :$, pero el código que escribas, si te sirve para un caso, te sirve para todos, incluidos aquellos casos en los que aparentemente no tengas que hacer modificaciones. Si uno de los casos no requiere modificaciones, esta definición estará guardada también en tu repositorio y al recuperarla, tu rutina así lo entenderá.

Ahora bien, el usuario selecciona la opción Reportes de Cliente aquí solo es necesario un Clic en el botón impresión sin mandar ningún cambio al reporte.  Sin embargo, pienso que el procedimiento de modificar debe de ejecutarse aunque no tenga nada que modificar.


Pienso como tú, el sistema debería ser general y ejecutarse en todos los casos, sabiendo en cada uno que hacer.

...Trate de explicarme mejor, al parecer los enrede un poco...

...Espero haber sido más claro.


Quizás si que seas claro y sea yo con mi desconocimiento del ReportBuilder el que no sepa interpretar correctamente.  :$ :)

Saludos.
  • 0

#8 Delphius

Delphius

    Advanced Member

  • Moderador
  • PipPipPip
  • 6.295 mensajes
  • LocationArgentina

Escrito 30 diciembre 2009 - 12:59

Hola Rolphy,
Desconozco Report Builder, por lo que en ese aspecto no puedo asesorarte. Lo que me llama la atención y en lo que tengo en duda es relacionado con lo que comentas:

Después de la llamada al método LoadFromDatabase es que se conocen cuales son los componentes que pertenecen al diseño seleccionado. Supongamos que el usuario ha seleccionado la opción de reporte llamada Estado de Cuenta donde se pide un rango de Fecha, esas fechas seleccionadas son las que quiero mostrar en la impresión; entonces debe existir un procedimiento donde yo le diga esto:




delphi
  1. Uses DmCustomReport;
  2.  
  3. TFrmEstadoCuenta.btnImprimir(Sender : TObject);
  4. begin
  5.   DmCustomReport.[b]Modificar[/b](ComponenteFecha, ValorAMostrar);
  6. end;




Como bien indica Escafandra el procedimiento que debe de existir en el DataModule donde reside el componente de Reporte debe de tener un FindComponent o cualquier otra forma para conocer los componentes que se van a modificar en Run-Time.

Ahora bien, el usuario selecciona la opción Reportes de Cliente aquí solo es necesario un Clic en el botón impresión sin mandar ningún cambio al reporteSin embargo, pienso que el procedimiento de modificar debe de ejecutarse aunque no tenga nada que modificar.

Espero haber sido más claro.


Léase las negritas que he colocado.
No termino de comprender. Tienes un método Modificar que debe ser capaz de modificar un componente (o al menos uno) que no necesariamente debe ejecutarse.

Primero indicas que en un supuesto caso de querer imprimir un Estado de Cuenta se necesita de un rango, y por tanto se debe modificar un componente. Por el otro existe otra opción de imprimir los Reportes de Cliente y que no requiere de modificación alguna.

Si es como estoy entendiendo, y existe un único componente "Reporte" en el cual se visualiza todos y cualesquiera de los tantos reportes disponibles, entonces el método Modificar debe ser capaz de determinar en que casos es necesario dicha modificación. Si es así, por tanto Modificar debe ser capaz de reconocer un diseño de otro a fin de aplicar los cambios adecuados (si es necesario).

¿Si en alguna ocasión no debe realizar cambio alguno, que sentido tiene que deba ejecutarse?

Por un lado entiendo que Modificar no sabe que informe es, que sólo se limita a modificar ciertas propiedades de algún componente. Entonces, antes de cada impresión deberá invocarse a dicho método. Algo así, a modo de ejemplo:



delphi
  1. procedure ButtonImprimirFactura(Sender: TObject);
  2. begin
  3.   DataModule.CargarReporte(Factura);
  4.   DataModule.Modificar(Componente, Valor);
  5.   DataModule.Imprimir;
  6. end;



En el ejemplo, se ilustra que se pide al DataModule que cargue el reporte de Factura, luego modifica algún componente y lo manda a imprimir.

En caso de que en otro reporte no sea necesario modificar algo simplemente no se lo invoca.

Eso es lo que me parece que has intentado ilustrar con tu ejemplo.

La pregunta aquí es si en realidad las modificaciones dependen o están relacionadas fuertemente con los reportes. Por ejemplo, si en algunos reportes es necesario modificar 20 labels, en otro 4 combos, en otro un pie de página y 6 logos, etc.

En caso de que todas las modificaciones tengan un denominador común (como por ejemplo: en realidad sólo se necesita modificar el título, un caption). Es posible esperar entonces un método muy simple y algo que con un simple FindComponent() bastaría.

Pero si estamos hablando de un escenario en donde las modificaciones dependen fuertemente del reporte es necesario un rediseño.
Quien tiene el poder, en principio, de leer el reporte adecuado tiene la información (Patrón "Experto en Información") y la capacidad (Patrón "Hacerlo yo Mismo") para determinar que cambios aplicar es el DataModule (según lo que desprendo de tus mensajes).
Debe dotarse de un mecanismo capaz de "traducir" reporte -> cambios.
Desde un punto exquisito de OO lo de esperar es una especie de "motor" de reportes. Esto se puede conseguir con algo como esto:

1. Disponer de una clase base "MotorReporte" con un método Modificar() virtual (quizás hasta abstracto).
2. Disponer de tantas clases descendientes de MotorReporte como tipos de informes existan. Cada una con un método Modificar() capaz de encontrar los diferentes tipos de controles y cambiar las propiedades adecuadas.
3. Existe una Fábrica FabricaMotorReporte que se encarga de crear las clases descendientes de MotorReporte.
4. Se asocia al DataModule con la clase que regresa la fábrica. Por tanto, el DataModule le indica a la fábrica cual necesita y ésta le regresa el objeto adecuado.
5. El DataModule envía la orden al motor creado de Modificar y luego envía el mensaje de Imprimir al reporte ya modificado.

Espero que se entienda.

Otra alternativa, similar a esta, es que sea el mismo DataModule quien asuma el rol de Fábrica. De este modo reducimos la cantidad de clases, aunque se pierde cierta cohesión. En este escenario es posible que el mismo método CargarReporte() quien, además de cargar el reporte, crea el objeto (después de todo... tiene la misma visibilidad de parámetro adecuada para lo que requiere el hipotético método CrearMotor() de una fábrica)

Es evidente que este enfoque del motor sólo es útil cuando la cantidad de reportes es mayor que la de los posibles cambios a realizar.

Me gustaría que Rolphy nos describiera con mayor precisión y profundidad sobre lo que está realizando. Me quedan muchas dudas sobre el enfoque que está siguiendo.

Saludos y disculpen el rollo.
  • 0

#9 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 30 diciembre 2009 - 04:13

Saludos.

Delphius estas cerca de lo que quiero conseguir, pero sin irnos tan profundo como para tener Factorías o Fabricas de clases no lo vislumbro tan grande, si en caso de necesitar algo tan grande pues tendría que modificar gran parte del código. Es mas bien como comentas al inicio de tu mensaje.

ReportBuilder permite tener un solo componente de reporte para la aplicación completa, claro teniendo en cuenta que se debe de almacenar los diseños en la Base de Datos o Archivos con extensión conocida por ellos; he decidido centralizar todo en un DataModule que contiene dicho componente con sus respectivas rutinas.  Existirá un método para cargar el reporte residente en el DataModule, que lo ejecutara la opción seleccionada por el usuario.

Dependiendo del reporte seleccionado es que debo de indicar cuales son los componentes que debo de modificar o en su defecto ninguno, los componentes a modificar por lo pronto serían Labels.

Lo que quiero lograr es un método que indique cuales son los labels y su valor a presentar, entonces otro que se ejecuta antes de la impresión, verifica cuales son los componentes, hace un FindComponent o cualquier otro forma de identificación y modifica los valores; en caso de no haber indicado ningún cambio pues el procedimiento sigue de largo y no ejecuta nada.

Espero haber sido más claro!
  • 0

#10 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 30 diciembre 2009 - 04:21

Saludos nuevamente.

Contestando la ultima parte de tu mensaje Delphius, con esto logro liberarme de tener que enviar el ejecutable cada vez que un cliente o usuario requería una modificación de un reporte X; simplemente envió el diseño y la aplicación se encarga de cargarlo a su repositorio y listo.

Además de poder tener versionamiento de diseños, que entiendo es una gran función añadida a una aplicación.
  • 0

#11 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 30 diciembre 2009 - 06:41

He leído lo que quieres hacer y no entiendo el problema.

Al final lo que se quiere es que cuando un cliente quiera modificar un informe, le puedas enviar un archivo con el informe, sin necesidad de actualizar toda la aplicación.

Lamentablemente no conozco Report Builder, pero no creo que sea muy diferente a FastReport, y con FastReport eso que quieres hacer es inmediato.

Al cliente le envías un archivo InformeAdaptado.frf y este archivo lo introduces en la base de datos, en un campo BLOB (igual que el resto de informes).

Ara cuando quieras imprimirlo ya solo tienes que cargar ese informe desde su archivo o desde la base de datos donde lo has almacenado (en FastReport usas un LoadFromStream para cargarlo de un campo Blob), y el usuario ya verá el informe con todas las modificaciones que hayas preparado para él (etiquetas, posiciones, campos, etc. ...) puesto que estas cargando el archivo .frf que adaptaste especificamente para él.

¿ Para que quieres detectar los componentes a modificar y sus nuevos valores ?. En el archivo que le enviaste con el Report, ya tiene esos valores modificados y así es como lo verá.

NOTA: Una cosa buena que tiene FastReport es que permite integrar de forma sencilla un editor de los Reports en tu aplicación, de forma que los mismos usuarios se los pueden personalizar (claro que muchas veces hacen un buen estropicio en el Report y nos llaman para que lo arreglemos, pero aún así es de mucha utilidad, especialmente para esos clientes que nunca estan satisfechos con los informes y siempre pedían modificaciones).

PD: Si tienes una serie de Labels que pueden variar su contenido, simplemente no definas ese contenido en el Report. Create una tabla de configuración, con campos para cada Label, con el valor predeterminado que van a tener por defecto (Ejplo. CONFIG_REPORT_ALBARAN_UNIDADES --> 'Unidades', CONFIG_REPORT_ALBARAN_DESCRIPCION --> 'Descripción', ... ...). De forma que en tu Report, en lugar de poner una cadena fija, indicas que muestren el valor de esos campos. Ahora si necesitas cambiar el contenido de algún label en un cliente determinado, solo tienes que modificar el contenido del campo que muestra (Ejplo. CONFIG_REPORT_ALBARAN_UNIDADES --> 'Num.').
  • 0

#12 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 30 diciembre 2009 - 08:47

Saludos.

Gracias Marc por interesarte, ReportBuilder cuenta con todas esas características que mencionas y veo que vas más o menos por donde quiero llegar.

Cuando menciono componente a modificar en Run-Time no es a crear nuevos diseños ni a cambiarles el actual para eso utilizare las funciones que mencionas Marc que ReportBuilder contiene también.

Al referirme a modificar es pasar valores como si fueran parámetros y estos desplegarían la información, entonces necesito decirle a alguien: "Mira el label1 tiene este Caption" del otro lado habrá alguien: "Ah me pasaron un label con este valor déjame modificarlo".

Y habrá ciertas circunstancia donde no le "diré nada a nadie", es todo lo que necesito, construir estos métodos.

Espero haberme expresado mejor!
  • 0

#13 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 31 diciembre 2009 - 04:32

En FastReport, eso que quieres hacer se llaman "Variables", y se puede definir su valor antes de cargar el Report. Pero la verdad es que no conozco Report Builder y no sé como se hace con él.

¿ No te sirve la idea de poner el contenido de todas las etiquetas que pueden variar en una tabla de la base de datos ?.
  • 0

#14 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 31 diciembre 2009 - 07:43

Gracias Marc.

ReportBuilder cuenta también con las "Variables", veré si por ahí consigo lo que necesito, en relación a una tabla, creo que tendría que analizar más de lo que realmente quiero, porque imagínate que sean varios conectados haciendo modificaciones a un mismo campo tendría que definir una serie de cosas para llevar el control cuando lo que necesito es simplemente desplegar una información que no es necesaria para nada mas.

Gracias por interesarte!
  • 0

#15 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.256 mensajes
  • LocationMéxico

Escrito 31 diciembre 2009 - 04:25

Hola

Con fastreport se trabajan las variables y los objetos de esta forma en tiempo de ejecución:



delphi
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   page1 : TfrxReportpage;
  4. begin
  5.   frxReport1.LoadFromFile('C:\Desarrollo\TDelphi\FastReport\RepExtensiones.fr3');
  6.   frxReport1.Variables.Variables['lblFecha'] := 'Desde: ' + DateTimePicker1.Text +
  7.                                 ' Hasta: ' + DateTimePicker2.Text;
  8.   page1:= frxreport1.findobject('page1')as TfrxReportpage;
  9.   page1.LeftMargin := 40;
  10.   frxReport1.ShowReport();
  11. end;



Espero te ayude.

Salud OS
  • 0

#16 Delphius

Delphius

    Advanced Member

  • Moderador
  • PipPipPip
  • 6.295 mensajes
  • LocationArgentina

Escrito 01 enero 2010 - 06:29

Hola,
He estado fuera estos últimos dos días y me he perdido de las novedades.
Debo decir que todavía me siento confundido... tengo que analizar y leer mejor el tema.

Saludos,

  • 0

#17 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 02 enero 2010 - 06:56

Saludos.

Al final resolví esto de la siguiente manera:

1.- Declare dos variables tipo TStringList, una para almacenar el componente y otra para el valor a asignar.

2.- Cree dos procedimientos, uno que se encarga de agregar los valores a los TStringList recibiendo dos parámetros de entrada; el segundo se ejecuta antes de la llamada al Print y verifica si el TStringList que contiene datos en caso de ser positivo pues realizo un FindComponent y listo.

Entonces desde la interfaz del usuario solo llamo el primer método para agregar los componentes que tiene ese diseño.

Si tienen alguna otra mejor idea, pues bienvenida sea!!!!
  • 0

#18 Delphius

Delphius

    Advanced Member

  • Moderador
  • PipPipPip
  • 6.295 mensajes
  • LocationArgentina

Escrito 02 enero 2010 - 11:58

Hola Rolphy,
Me alegro de que lo hayas resuelto.

Yo tengo una pregunta, ¿entonces modificas siempre una misma propiedad?
¿En el TStringList que guardas, la referencia al objeto (mediante .Objects o .AddObject) o el nombre?

Esto te lo pregunto porque si es lo segundo te puedes evitar el segundo TStringList. La clase base TStrings tiene la propiedad Values de tipo vectorial y puede emplearse para referenciar cualquier posición por un nombre. De este modo se puede asociar a cada posición por el nombre del componente.

Por ejemplo:



delphi
  1. lista := TStringList.Create;
  2. lista.Values['CASA'] := 'roja';
  3. lista.Values['HOTEL'] := 'azul';
  4. lista.Values['DEPTO'] := 'verde';
  5. lista.Values['MUSEO'] := 'celeste';
  6. ...



Provoca que existan 4 elementos. Cada uno se referencia por el nombre, y el valor está destinado para guardar los colores de cada edificación. Es un ejemplo simple pero creo que ilustra la idea. ;)

Saludos,
  • 0

#19 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 02 enero 2010 - 12:02

Saludos.

Muchas gracias Delphius, guardo el nombre nada más, no puedo guardar el objeto porque no lo tengo sino hasta después de cargar el diseño del reporte.  Además como mando el nombre y dentro del procedimiento que aplica los cambios hago un FindComponent y si no esta nil realizo dicha modificación.

Probare el asunto del value.
  • 0