20 septiembre 2006

Migrando de Omnis 7 a Omnis Studio

Desde la aparición 1997 de Omnis Studio, Raining Data ha esperado que los desarrolladores dejaran Omnis 7 y adaptaran sus aplicaciones al nuevo Omnis Studio, pero realmente se trataba de una labor ardua, puesto que representaba todo un cambio en la filosofía de programación (Programación OO).

Seguramente debido en parte a esto y en otra al desembolso económico necesario, muchos desarrolladores continuaron manteniendo sus aplicaciones en Omnis 7, alentados también por el compromiso de Raining Data a dar continuidad a dicho producto.

Pero con la llegada de los Mac Intel, Raining Data anuncia que no seguirá dando soporte a Omnis 7 para la nueva plataforma Mac, es decir que no hay actualización para Mac OS X, ni la habrá.

¿Qué haremos ahora con nuestras actuales aplicaciones en Omnis 7, bajo plataforma Mac?

Posibilidades:

1) Cambiar de plataforma, es decir pasar de Mac OS X a Windows

2) Convertir la aplicación a Omnis Studio

La primera opción puede que le atraiga a usted si es desarrollador, pero no creo que resulte muy popular entre los usuarios de ordenadores Macintosh, entre los cuales me incluyo.

La segunda opción parece la más razonable y cómoda para el usuario, pues aunque suponga un esfuerzo adicional por parte del desarrollador, el resultado final será de mayor agrado al usuario, además Omnis Studio proporciona mayor funcionalidad y flexibilidad, ahorrando en tiempo de desarrollo y costes, además de ser la herramienta idónea, para entrar en el llamado “e-business”, Internet e Intranets.

¿Cómo migraremos nuestra antigua aplicación Omnis 7 a Omnis Studio?

Raining Data asegura que usted puede realizar esta tarea con no mucho esfuerzo de su parte (ver http://www.omnis.net/products/omnis7/conversion.html) pero mi experiencia es que se trata de una ingente y eso sólo para que funcione. Si lo que queremos es que además aproveche toda la funcionalidad de Omnis Studio, la tarea puede llevar hasta semanas de arduo trabajo.

¿Qué otra opción tengo?

Por supuesto usted puede solicitar ayuda externa, migrar de Omnis 7 a Omnis Studio bajo plataforma Mac OS X o Windows, incluso migrar de Mac OS X a Windows o de Windows a Mac OS X, puede ser mucho más sencillo para usted.

Pídame presupuesto sin compromiso alguno, usted sólo tendrá que enviarme su librería Omnis 7 y tendrá a vuelta de correo una nueva librería Omnis Studio completamente funcional y adaptada a las nuevas funcionalidades y filosofía de trabajo en Omnis Studio.

Póngase en contacto conmigo para más información.

18 julio 2006

Actualización automática de bibliotecas a los usuarios finales

Problemática

A menudo los desarrolladores realizamos cambios sobre las bibliotecas Omnis Studio para adaptarlas a las necesidades de los usuarios o para corregir posibles deficiencias, esto obliga a actualizar las versiones que estos tiene instaladas. Se necesita por tanto un procedimiento que actualice de forma automática los ordenadores de los usuarios.

Escenario previo (Requerimientos):

Partimos de la base de que disponemos de un servidor FTP, donde existe un directorio llamado “ForUpdate”, que contendrá una copia de las bibliotecas a actualizar, es el lugar donde el desarrollador dejará la nueva biblioteca, cada vez que se produzca un cambio en la misma y por tanto debe ser actualizada al usuario.

Resolución:

Ahora construiremos una nueva biblioteca llamada “Actualiza” en nuestro ejemplo y que una vez terminada situaremos en la carpeta “startup” de Omnis Studio, la idea es que sea la primera biblioteca que Omnis Studio ejecutará.

Todos los procedimientos descritos pertenecen a la clase Task “Startup_Task”

Las variables del caso son las siguientes:

No. Instance Variable Type Subtype Init.Val/Calc

1 iActualizado Boolean
2 iArchivo Character 100
3 iDirArchi Character 100
4 iDirFTP List
5 iDirLocal List
6 iFTPSocket Long integer
7 iPassword Character 100 ‘******’
8 iServerDirec Character 100 '192.33.20.20'
9 iServerError Character 500
10 iUsuario Character 100 'librerias'


En primer lugar construiremos el método $construct de la librería “Actualiza”, todos los procesos que tendrán lugar se ejecutarán desde este procedimiento.

Después de haber definido las variables del caso, debemos hacer lo mismo con las siguientes variables locales:


No. Local Variable Type Subtype

1 lErrCode Short integer (0 to 255)
2 lExportPath Character 100
3 ok Boolean

Además del método público $construct, crearemos un total de seis métodos privados: EnFTP, EnUsuario, Guardarropa, Compara, SolicitaCopia y CopiaFTP.

El método $construct se compondrá finalmente de las siguientes líneas de código:

;; obtiene el contenido del directorio FTP a actualizar
1 Do method EnFTP Returns ok
2 If ok
;; obtiene el contenido del directorio correspondiente a la última actualización
3 Do method EnUsuario
;; selecciona de la lista FTP los archivos que necesitarán actualizarse
4 Do method Compara
;; pregunta al usuario y permite la copia de la nueva versión de la biblioteca
5 Do method SolicitaCopia
6 If iActualizado
;; guarda la lista de archivos FTP en un documento local denominado UltimoUpdate.txt
7 Do method Guardaropa
8 End If
9 End If

El procedimiento “EnFTP”, tiene como objetivo leer la entrada del directorio de los archivos contenidos en el servidor FTP y guardarlo sobre la lista “iDirFTP”, para posteriormente compararla con las entradas de directorio correspondientes a la última actualización que realizo el usuario.


No. Local Variable Type Subtype

1 lErrCode Short integer (0 to 255)

1 ; establece la conexión con el servidor FTP
2 FTPConnect (iServerDirec,iUsuario,iPassword) Returns iFTPSocket
3 If iFTPSocket; entra en el directorio "ForUpdate"
8 FTPCwd (iFTPSocket,'ForUpdate') Returns lErrCode
9 If lErrCode
10 OK message Error FTP {[con("Error al acceder al directorio FTP de
actualización ",kCr,"Código : ",lErrCode)]}
11 Else
12 Do iDirFTP.$define(iDirArchi)
13 ; devulve la lista de archivos en el directorio FTP sobre la variable iDirFTP en formato extendido
14 FTPList (iFTPSocket,iDirFTP,,1) Returns lErrCode
15 If lErrCode
16 FTPGetLastStatus (iServerError) Returns lErrCode
17 OK message Error FTP {[con("Error obteniendo la lista de archivos del
servidor FTP",kCr,"Devolvio: ",kCr,iServerError)]}
18 Quit method kFalse
19 Else
20 Quit method kTrue
21 End If
22 End If
23 End If

El procedimiento “EnUsuario”, la idea es compara la lista de los archivos a actualizar (servidor FTP), con los de la última actualización realizada, pero note que en caso de tratarse de la primera actualización dicha lista no existirá, por lo que deberá ser creada.

Finalmente la lista se guardará en un archivos de texto local denominado “UltimoUpdate.txt” y es desde este archivo que se cargará la lista de archivos para su comparación, la lista de archivos contenida en “UltimoUpdate.txt” se guardará en la variable de lista “iDirLocal”.

Finalmente y a estas alturas del proceso abremos obtenido dos listas, “iDirFTP” (con el directorio del servidor FTP) e “iDirLocal” (con el directorio correspondiente a la última actualización)

No. Local Variable Type Subtype

1 lExportPath Character 100

1 ; confecciona una lista con los últimos archivos actualizados
2 Calculate lExportPath as con(sys(115),'UltimoUpdate.txt')
3 Test if file exists {[lExportPath]}
4 If flag false
5 Do method Guardaropa ;; guarda la lista de archivos FTP en un documento
local denominado UltimoUpdate.txt
6 End If
7 Do iDirLocal.$define(iDirArchi)
8 Set import file name {[lExportPath]}
9 Prepare for import from file {Delimited (commas)}
10 Import data iDirLocal
11 End import
12 Close import file

El procedimiento “Guardarropa”, tiene como único objetivo actualizar el archivo local “UltimoUpdate.txt”, que como ya sabe el lector, contiene las entradas del directorio FTP, correspondientes a la última actualización.

No. Local Variable Type Subtype

1 lExportPath Character 100

1 Calculate lExportPath as con(sys(115),'UltimoUpdate.txt')
2 Set print or export file name {[lExportPath]}
3 Prepare for export to file {Delimited (commas)}
4 Export data iDirFTP
5 End export
6 Close print or export file

El procedimiento “Compara”, halla las diferencias entra las listas “iDirFTP” e “iDisLocal”, el resultado final es que únicamente quedan seleccionadas las entradas del directorio FTP, que deberán ser actualizadas (“iDirFTP”)

No. Local Variable Type Subtype

1 lComapara Character 100

1 Set current list iDirLocal
2 For each line in list from 1 to #LN step 1
3 Load from list
4 Calculate lComapara as iDirArchi
5 Do iDirFTP.$search(iDirArchi=lComapara,kTrue,kFalse,kTrue,kFalse)
6 End For
7 Set current list iDirFTP
8 Invert selection for line(s) (All lines)

A continuación describimos el procedimiento “SolicitaCopia”, cuya única misión es solicitar al usuario el lugar de destino para albergar la nueva biblioteca actualizada.

1 Calculate iActualizado as kFalse
2 For each line in list (Selected lines only) from 1 to #LN step 1
3 Load from list
4 Calculate iArchivo as right(iDirArchi,len(iDirArchi)-pos(':',iDirArchi)-3)
5 Yes/No message ¡Nueva versión! (Icon,Sound bell) {Existe una nueva versión
de [iArchivo][kCr]¿Desea instalarla ahora?}
6 If flag true
7 Do method CopiaFTP
8 End If
9 End For

Finalmente tan sólo nos queda describir el último de los procedimientos “CopiaFTP”, que es donde finalmente se copiaran los archivos o biblioteca a actualizar desde el servidor FTP al ordenador del usuario. La variable “iActualizado”, nos permitirá saber si todo el proceso termino con éxito.

Note que sólo si todo el proceso termina con éxito se actualizará el archivo “UltimoUpdate.txt”, de este modo se podría volver a intentar la actualización tantas veces como se requiera.


No. Local Variable Type Subtype

1 lError Short integer (0 to 255)
2 lPath Character 500

1 Do FileOps.$selectdirectory(lPath,con('Elija destino para ',iArchivo)) Returns
lError
2 If lError=1
3 FTPType (iFTPSocket,1) Returns iServerError
4 If not(iServerError)
5 Calculate lPath as con(lPath,':',iArchivo)
6 Working message Descargando.../-1073735809,-1073735805;50;0;60 {Copiando
librería [iArchivo]...}
7 FTPGet (iFTPSocket,iArchivo,lPath) Returns iServerError
8 If iServerError
9 OK message Error FTP {[con("Error al copiar el archivo
",upp(iArchivo),kCr,"Código de error : ",iServerError)]}
10 Calculate iActualizado as kFalse
11 Else
12 Calculate iActualizado as kTrue
13 End If
14 Else
15 Calculate iActualizado as kFalse
16 End If
17 Else
18 Calculate iActualizado as kFalse
19 End If

Bueno… esto ha sido todo, se trata de la primera entrega de las muchas cosas que deseo compartir con todos los desarrolladores de Omnis Studio.

Cuento con vuestro ánimo para seguir poniendo mis experiencias, en este blog.

¿Cómo crear, mantener y visualizar un archivo "log"?

Casi todas las aplicaciones cuentan con un archivo "log", donde guardar las incidencias que se vayan produciendo durante su ejecución, normalmente se trata de un archivo de texto "txt" con varias columnas tabuladas.


A continuación se muestra como crear, mantener y visualizar un archivo "log" para nuestra librería Omnis Studio, se describen un total de tres procedimientos:

1) Crear archivo 2) Añadir entradas 3) Visualizar contenido.

1) Método para crear el archivo, (toma su nombre del que ya tiene la librería)

Calculate ArchivoLog as con(mid(sys(10),1,len(sys(10))-3),'log')
Do FileOps.$doesfileexist(ArchivoLog) Returns ExisteLog
If not(ExisteLog)
Do FileOps.$createFile(ArchivoLog)
End If

2) Método para añadir entradas al archivo (Denominado: $grabalog)

Variable de parámetro

"PTextoLog" de tipo Carácter (Contiene el texto a grabar en el archivo de log)

; Inserta una línea en el archivo de log, con el texto contenido en "PTextoLog"
Calculate lPathname as con(mid(sys(10),1,len(sys(10))-3),'log')
Do FileLog.$openfile(lPathname)
Do FileLog.$getfilesize() Returns FileEnd
Do FileLog.$writefile(con(#D,' ',tim(#T,'H:N:S'),chr(9),PTextoLog,chr(9), iUserName,chr(13)),FileEnd)
Do FileLog.$closefile()

Ejemplo de llamada al método "$grabalog"

Do method $grabalog (con('Sesión iniciada con éxito en ',iSessionName))

Esto añadiría una nueva entrada (el texto se pasa en el parámetro "PTextoLog")

3) El siguiente procedimiento transfiere el contenido del archivo de log a una lista.

Set current list lListaLog
Define list {IFechaLog,ITextoLog,IUsuarioLog}
Calculate lPathname as con(mid(sys(10),1,len(sys(10))-3),'log')
Set import file name {[lPathname]}
Prepare for import from file {Delimited (tabs)}
Import data lListaLog
End import
Close import file

Y esto es todo, sencillo pero eficaz, sigo animando a todos los lectores de este blog a que dejéis vuestros animadores comentarios, eso permitirá que continúe añadiendo mis experiencias al mismo.

Ya sabéis que también podéis contactar conmigo a través de Skipe e incluso (si así lo deseáis) podríais colaborar con la publicación de vuestras propias experiencias, sería estupendo que este blog se convirtiera en un punto de encuentro para todos los que “nos pegamos” con el desarrollo de aplicaciones en Omnis Studio.

Programación OO con Omnis Studio

Los ánimos recibidos por quienes habéis consultado este blog (como el "capitanandres") han sido suficientes para inclinarme por publicar algo más en el "blog", esta sería la primera entrega de lo que pretendo sea un curso de aprendizaje que arroje algo de luz para todos los desarrolladores Omnis Studio de habla hispana.

Sería agradable ver vuestros comentarios en el “blog”, sobre los artículos que publico, eso ayudaría bastante.

Consultas (SQL) dinámicas y sencillas

La capacidad que Omnis Studio nos proporciona para trabajar con objetos, será usada en este práctico ejemplo, para que el programador pueda construir nuevas consultas (query’s) sobre la base de datos, sin que sea necesario alterar el código de la aplicación, al añadir o modificar una consulta.

Puede descargar la librería Ejemplo.lib en este link.

Para nuestro ejemplo partimos de una tabla o esquema ALUMNOS, para el cual creamos un objeto de tipo “Query”, este será la base que usará la aplicación para construir la “interface” de búsqueda al usuario.

Posteriormente el programador se limitará a crear y/o modificar nuevos objetos “Query”, pero no necesitará escribir nuevo código.

En nuestro ejemplo crearemos la “Query” “Bus_Alumno” como sigue:

Detalle de la consulta: Bus_Alumno

Esquema Columna....Alias
ALUMNOS NUMEXPALU..Expediente
ALUMNOS APEL1......Primer apellido
ALUMNOS APEL2......Segundo apellido
ALUMNOS NOMBRE.....Nombre
ALUMNOS DNI........DNI
ALUMNOS FALTAN.....Faltan
ALUMNOS AVISO......Aviso
ALUMNOS ROWID......Puntero

Extensión: (Almacena la última consulta efectuada por el usuario)

La columna “Alias” contendrá la denominación que será usada para construir el “pop-menú” desde donde el usuario seleccionará el campo de comparación.

Además de las columnas o datos seleccionados del esquema “ALUMNOS”, se incluye la columna “ROWID”, para permitir la selección de una fila única por parte del usuario (Véase ventana de “Resultados”)

La ventana de búsqueda es auto-ampliable, permitiendo la inclusión de hasta 10 valores de comparación, su aspecto totalmente desplegada sería el que se muestra más bajo. Cabe destacar que el objeto “Bus_Alumno” guardará siempre la última consulta realizada por el usuario, incluso aunque abandone la aplicación.


A continuación mostramos las variables asociadas a la clase (Ventana “Buscar”) y los métodos asociados:
Variables, tipo y uso
bCampos1…bCampos10, tipo: Lista, Uso: Lista de los datos objeto de comparación (Nombres alias)
bCompara1…bCompara10, tipo: Lista, Uso: Lista de opciones de comparación
Valor1…Valor10, tipo: Carácter, Uso: Guarda el primer posible valor de comparación establecido

pQuery, tipo: Carácter Contiene una copia de las condiciones de búsqueda establecidas
Método: $construct
Variables locales
CuentaClon, tipo: Entero, Uso: Usado para crear el duplicado de las listas "bCampos" y "bCompara"
Item, tipo: Referencia, Uso: Referencia al primer dibujo a fijar en el fondo de la ventana
Ref_Col, tipo: Referencia, Uso: Referencia a cada columna en la "query" de búsqueda para la vista en selección
Variables parámetros
Busqueda, tipo: Carácter, Uso: Contiene el nombre de la "query" de búsqueda para la vista en selección
; Crea desplegables para los campos de búsqueda
Do bCampos1.$define(#S1,#S2)
Do bCampos1.$clear()
Do bCampos1.$add($clib.$queries.[Busqueda].$objs.$first().$alias,$clib.
$queries.[Busqueda].$objs.$first().$colname)
Set reference Ref_Col to $clib.$queries.[Busqueda].$objs.$first()
While Ref_Col
Set reference Ref_Col to $clib.$queries.[Busqueda].$objs.$next(Ref_Col)
If Ref_Col.$alias<>''&Ref_Col.$alias<>'Puntero'
Do bCampos1.$add(Ref_Col.$alias,Ref_Col.$colname)

End If
End While
Do bCampos1.$line.$assign(1)
For CuentaClon from 2 to 10 step 1
Do bCampos[CuentaClon].$copydefinition(bCampos1)
Do bCampos[CuentaClon].$merge(bCampos1,,,kTrue)
Do bCampos[CuentaClon].$line.$assign(1)
End For
; Crea desplegables para las comparaciones de búsqueda
Do bCompara1.$define(#S1)
Do bCompara1.$clear()
Do bCompara1.$add('contiene')
Do bCompara1.$add('empieza por')
Do bCompara1.$add('es')
Do bCompara1.$add('no es')
Do bCompara1.$add('mayor o igual a')
Do bCompara1.$add('menor o igual a')
Do bCompara1.$line.$assign(1)
For CuentaClon from 2 to 10 step 1
Do bCompara[CuentaClon].$copydefinition(bCompara1)
Do bCompara[CuentaClon].$merge(bCompara1,,,kTrue)
Do bCompara[CuentaClon].$line.$assign(1)
End For
Clear range of fields Valor1 to Valor10
Do $cwind.$objs.Opciones.$objs.Valor1.$enabled.$assign(kTrue)
; Pasa el parámetro de búsqueda a la variable global "pQuery" para no perderlo
Calculate pQuery as Busqueda
Método: PasaValor
Variables parámetros
Valor, tipo: Carácter, Uso: Contiene el valor a comparar
; Pasa el símbolo de consulta
Switch lst(#S1)
Case 'contiene'
Calculate bCondicion as con(bCondicion," LIKE '%",Valor,"%' ")
Case 'empieza por'
Calculate bCondicion as con(bCondicion," LIKE '",Valor,"%' ")
Case 'es'
Calculate bCondicion as con(bCondicion," = '",Valor,"' ")
Case 'no es'
Calculate bCondicion as con(bCondicion," <> '",Valor,"' ")
Case 'mayor o igual a'
Calculate bCondicion as con(bCondicion," >= '",Valor,"' ")
Case 'menor o igual a'
Calculate bCondicion as con(bCondicion," <= '",Valor,"' ")
End Switch
Botón “Más opciones”: $event
On evClick ;; Amplia el número de opciones en la ventana
If $cwind.$objs.Opciones.$height<>345
Do $cwind.$objs.Opciones.$height.$assign($cwind.$objs.Opciones.$height+33)
Do $cwind.$height.$assign($cwind.$height+33)
Switch $cwind.$objs.Opciones.$height
Case 81
Do $cwind.$objs.Opciones.$objs.Valor2.$enabled.$assign(kTrue)
Case 114
Do $cwind.$objs.Opciones.$objs.Valor3.$enabled.$assign(kTrue)
Case 147
Do $cwind.$objs.Opciones.$objs.Valor4.$enabled.$assign(kTrue)
Case 180
Do $cwind.$objs.Opciones.$objs.Valor5.$enabled.$assign(kTrue)
Case 213
Do $cwind.$objs.Opciones.$objs.Valor6.$enabled.$assign(kTrue)
Case 246
Do $cwind.$objs.Opciones.$objs.Valor7.$enabled.$assign(kTrue)
Case 279
Do $cwind.$objs.Opciones.$objs.Valor8.$enabled.$assign(kTrue)
Case 312
Do $cwind.$objs.Opciones.$objs.Valor9.$enabled.$assign(kTrue)
Case 345
Do $cwind.$objs.Opciones.$objs.Valor10.$enabled.$assign(kTrue)
End Switch
End If
Botón “Menos opciones”: $event
On evClick ;; Reduce el número de opciones en ventana
If $cwind.$objs.Opciones.$height<>48
Do $cwind.$objs.Opciones.$height.$assign($cwind.$objs.Opciones.$height-33)
Do $cwind.$height.$assign($cwind.$height-33)
Switch $cwind.$objs.Opciones.$height
Case 48
Do $cwind.$objs.Opciones.$objs.Valor2.$enabled.$assign(kFalse)
Calculate Valor2 as ''
Case 81
Do $cwind.$objs.Opciones.$objs.Valor3.$enabled.$assign(kFalse)
Calculate Valor3 as ''
Case 114
Do $cwind.$objs.Opciones.$objs.Valor4.$enabled.$assign(kFalse)
Calculate Valor4 as ''
Case 147
Do $cwind.$objs.Opciones.$objs.Valor5.$enabled.$assign(kFalse)
Calculate Valor5 as ''
Case 180
Do $cwind.$objs.Opciones.$objs.Valor6.$enabled.$assign(kFalse)
Calculate Valor6 as ''
Case 213
Do $cwind.$objs.Opciones.$objs.Valor7.$enabled.$assign(kFalse)
Calculate Valor7 as ''
Case 246
Do $cwind.$objs.Opciones.$objs.Valor8.$enabled.$assign(kFalse)
Calculate Valor8 as ''
Case 279
Do $cwind.$objs.Opciones.$objs.Valor9.$enabled.$assign(kFalse)
Calculate Valor9 as ''
Case 312
Do $cwind.$objs.Opciones.$objs.Valor10.$enabled.$assign(kFalse)
Calculate Valor10 as ''
End Switch
Do $redraw()
End If
Botón “Buscar”: $event
Variables locales
ItemAnterior, tipo: Entero, Uso: Contiene el nombre del campo a comparar, seleccionado en la línea anterior
Replica, tipo: Entero, Uso: Usado para recorrer cada una de las 10 líneas posibles de comparación
On evClick ;; Compone la consulta sobre la variable "bCondicion"
Calculate bCondicion as ''
If Valor1<>''
Calculate bCondicion as 'where '
Set current list bCampos1
Calculate ItemAnterior as bCampos1.$line
Calculate bCondicion as con(bCondicion,bCampos1.#S2)
Set current list bCompara1
Do method PasaValor (Valor1)
End If
For Replica from 2 to 10 step 1
If Valor[Replica]<>''
Set current list bCampos[Replica]
If bCampos[Replica].$line=ItemAnterior
Calculate bCondicion as con(bCondicion,' OR ')
Else
Calculate bCondicion as con(bCondicion,' AND ')
End If
Calculate ItemAnterior as bCampos[Replica].$line
Calculate bCondicion as con(bCondicion,bCampos[Replica].#S2)
Set current list bCompara[Replica]
Do method PasaValor (Valor[Replica])
End If
End For
; Pasa la misma consulta a la ventana de vista
Calculate $clib.$queries.[pQuery].$extraquerytext as bCondicion
Do $topwind.$close()
Esto será todo lo necesario para que el usuario construya una búsqueda, el resultado será una consulta SQL, preparada para ser lanzada a la base de datos.
Ahora nos centraremos en como presentar el resultado de la petición al usuario, para esto usaremos la clase ventana “Resultados”, a continuación mostramos el aspecto de esta ventana, así como sus variables y procedimientos.



Esta ventana tiene como finalidad mostrar los resultados de la última búsqueda realizada por el usuario. En la parte superior se recuerda la consulta realizada.
Variables asociadas a esta clase
iBusqueda, tipo: Carácter, Uso: Contiene una copia del nombre de la "query" en selección
Método: $construct
Variables locales
Item, tipo: Referencia, Uso: Referencia al primer dibujo a fijar en el fondo de la ventana
lStatus, tipo: Entero, Uso: Recoge el resultado de la sentencia "fetch"
Nom_Col, tipo: Carácter, Uso: Contiene los nombres alias de las columnas en la "query" de búsqueda en selección
Ref_Col, tipo: Referencia, Uso: Referencia a cada columna en la "query" de búsqueda para la vista en selección
Variables parámetros
Busqueda, tipo: Carácter, Uso: Contiene una el nombre de la "query" en selección
Calculate Nom_Col as $clib.$queries.[Busqueda].$objs.$first().$alias
Set reference Ref_Col to $clib.$queries.[Busqueda].$objs.$first()

For #1 from 2 to $clib.$queries.[Busqueda].$objs.$count()-1 step 1

Set reference Ref_Col to $clib.$queries.[Busqueda].$objs.$next(Ref_Col)

Calculate Nom_Col as con(Nom_Col,",",Ref_Col.$alias)

End For

Calculate $cwind.$objs.Resultados_headedlistbox_1023.$columnnames as Nom_Col

Calculate $cwind.$objs.Resultados_headedlistbox_1023.$colcount as
$clib.$queries.[Busqueda].$objs.$count()-1
Do iVerList.$definefromsqlclass(Busqueda)

Do iVerList.$sessionobject.$assign(iSessionObj) Returns #F

Do iVerList.$select() Returns lStatus

Do iVerList.$fetch(kFetchAll) Returns lStatus

Do iVerList.$line.$assign(1)
; Pasa el parámetro de busqueda a la variable de clase "iBusqueda" para no perderlo
Calculate iBusqueda as Busqueda
Do $root.$redraw()
Acciones sobre la lista: $event
Variables locales
Nom_Col, tipo: Carácter, Uso: Contiene el nombre de la columna seleccionada
Ref_Col, tipo: Referencia, Uso: Referencia a cada columna de la "query" en selección
On evDoubleClick
Do $topwind.$close()
On evHeaderClick ;; Clasifica los elementos según la columna seleccionada
Switch pColumnNumber
Case 1
Calculate Nom_Col as con("iVerList.",$clib.$queries.[iBusqueda].$objs.$first().$alias)
Do iVerList.$sort([Nom_Col])
Default
Set reference Ref_Col to $clib.$queries.[iBusqueda].$objs.$first()
For #1 from 2 to pColumnNumber step 1
Set reference Ref_Col to $clib.$queries.[iBusqueda].$objs.$next(Ref_Col)
Calculate Nom_Col as con("iVerList.",Ref_Col.$alias)
Do iVerList.$sort([Nom_Col])
End For
End Switch
Do $cwind.$redraw()
Sigo animando a todos los lectores de este blog a que dejéis vuestros animadores comentarios, eso permitirá que continúe añadiendo mis experiencias al mismo.

Ya sabéis que también podéis contactar conmigo a través de Skipe e incluso (si así lo deseáis) podríais colaborar con la publicación de vuestras propias experiencias, sería estupendo que este blog se convirtiera en un punto de encuentro para todos los que “nos pegamos” con el desarrollo de aplicaciones en Omnis Studio.

Ejemplo de conexión a Oracle

Se trata de una librería que permite conectarse a una base de datos Oracle, e interactuar con ella. Puede ser modificada para usarla con otras bases de datos (no usa esquemas y/o tablas), también incluyo una comparativa entre Oracle y MySQL.

Librería ejemplo
Comparativa