30 noviembre 2006

Números contenidos en otros

Si habéis trabajado con las APIs u otros lenguajes de programación conoceréis que determinados parámetros de muchas funciones están compuestos por la suma de más de un número, de tal modo que dicho número posteriormente permite conocer cuales son los que le componen. Para empezar, los números utilizados para conseguir la suma deben ser el doble que el anterior o si se representan en binario solo los que tengan el primer bit a uno:
  1          1
  2         10
  4        100
  8       1000
 16      10000
 32     100000
 64    1000000
128   10000000

Según lo anterior, un parámetro que contenga el número 145 estará compuesto por los números 128 + 16 + 1, otros entornos tienen su propia función para determinar si un número esta contenido en otro (por decirlo de algún modo), y no es el caso de PowerBuilder, por lo que tendremos que crearnos nuestra propia función para determinar esto, y así poder utilizar este tipo de números para trabajar con Apis o para nuestro uso directo. Según el ejemplo anterior el 145 en binario sería 10010001, de tal modo que es fácil determinar si el número decimal 16 (en binario 10000) esta dentro del 145, pasando ambos a binario y comprobando si la posición (de derecha a izquierda) del bit a 1 del número buscado, también es 1 en el número en el que se busca:

10010001 número en el que se busca
00010000 número buscado

Con la lógica seguida hasta este punto ya solo queda pasar ambos número a binario, por lo que, de modo elegante, sería una función para la operación de conversión y otra para la comparación del bit:



Convertir en binario - string xx_aBinario(ulong aul_numero):



Comparación del bit - contieneNumero(ulong aul_numeroContenedor,ulong aul_numeroContenido):



Este es un método como otro cualquiera, hay muchos otros, recuerdo que antes de conocer el tema de los bit en los binarios me dedicaba a montar un bucle donde obtenía el número mayor doble del anterior partiendo desde uno y que fuera inferior al numero contenedor, el resultado era el mayor contenido, después al restar al contenedor el número mayor contenido, repetía la operación y obtenía así cada uno de los contenidos en la suma, claro esta que en cuanto aparecía el que buscaba salia, en caso contrario indicaba que no estaba contenido... y esto sigue, si os dais una vuelta por las PFCs encontrareis otro método.

Como siempre, espero que os sirva.

30 octubre 2006

Trabajar con años y meses comerciales

He podido ver en más de un programa como se trabaja con formulas para cálculos de interés con años y meses comerciales (en adelante calendario comercial) y luego para realizar operaciones entre fechas o liquidaciones se utiliza el calendario natural, por lo que al final no corresponden los resultados finales. La cuestión de todo el descabale se basa en que se utilizan funciones convencionales para calcular la diferencia entre dos fechas, cuando en realidad este tipo de formulas no funcionan, ya que asignan a cada mes su día final correcto según el calendario natural, pero el comercial, siempre debe tener 30 días, tanto si es febrero, como bisiesto o tiene 31 días.

Para evitar esto desarrollé un método que determina diferencia entre fechas utilizando el calendario comercial y con un par de funciones extras.

El método utiliza como argumentos iniciales dos fechas (desde y hasta), que son entre las que se tiene que calcular la diferencia, por otro lado, aparece otro parámetro de tipo boleano que permite indicar si hay que realizar ajuste por día de inicio o final de mes de la fecha hasta... no me parece claro como lo explique, así que un ejemplo: Si el día de la fecha "desde" es superior al día de la fecha "hasta" y el día de la fecha "hasta" es final de mes, se realiza un ajuste para que cuadre el resultado en meses. Ahora con fechas y números reales, si a una fecha inicial 30/12/2006 le sumamos dos meses (que no 60 días) obtenemos una fecha real final 28/02/2007, si descomponemos ambas fechas pasando a días cada una de sus partes y obtenemos la diferencia, nos arroja en este caso un resultado de 58, sin embargo en este caso para cálculos comerciales, me puede interesar que nos de los 60 días correspondientes a los 2 meses comerciales, ya que el fecha final es la que es porque no existe 30/02/xxxx. Este caso se detecta comprobando que la fecha "hasta" es un final de mes y que el día de la fecha "desde" es superior al día de la fecha "hasta", para lo que habría que indicar que se tengan en cuenta los días de diferencia entre ambas fechas en el resultado final.

Tener en cuenta si en nuestras cuentas de días se deben considerar los dos días pasados como argumentos o solo uno ellos. Fijaros que en el primer caso del 15/xx/xxxx al 15/xx+1/xxxx transcurren 31 días y no 30. Para considerar este último comentario en el algoritmo resultante solo tendríais que añadir, o no, una unidad al total resultante. Todo esto queda del siguiente modo:



Como siempre, espero que os sea de utilidad al resto.

29 septiembre 2006

Crear objetos de Forma dinámica en un UserObject

Este post se origina con la respuesta que he dado en un foro de Sybase a este tema. Me parece interesante dejarlo aquí para que no se quede en el tintero, luego es muy difícil localizar temas antiguos en los foros.

La problemática surge de que en PB no se puede añadir un objeto, creado de modo dinámico, sobre un objeto de usuario visual. Sin embargo esto si se puede hacer sobre una ventana, así que utilizaremos esta opción para lograr lo que PB no nos permite hacer directamente.

Como se puede resolver... pues muy sencillo, hacemos el CREATE del objeto que queremos poner dinámicamente, le asignamos false a la propiedad visible y utilizamos la función OpenUserObject para pegarlo sobre la propia ventana donde se encuentra nuestro objeto visual, como el objeto esta invisible no se ve, después cambiamos mediante una función de API el padre del objeto creado dinámicamente y le asignamos como nuevo padre nuestro objeto visual y para terminar y antes de cambiar el atributo de visible, asignamos las propiedades que queremos al objeto en cuestión... Así se pierde mucho uno, por lo que lo mostraré con código. Reutilizaré el del post con las modificaciones necesarias para que funcione correctamente. Para poneros en antecedentes se trata de un objeto visual de usuario que se utilizará de barra de herramientas:

VARIABLES DE INSTANCIA DEL UO_BARRA_BOTONES:



DECLARACIÓN DE FUNCIÓN EXTERNA:



CONSTRUCTOR DEL UO_BARRA_BOTONES:



MÉTODO ADD:



Espero que os sirva a todos.

16 agosto 2006

VScrollBar siempre visibles

Cuando la cantidad de páginas mostradas en un dw es solo una, la barra de desplazamiento vertical no aparece y en ocasiones estéticamente puede molestar. PowerBuilder no permite dejar la barra siempre visible y que esta este activa o no según corresponda, pero podemos simularlo utilizando APIS de Windows:

Declaración de funciones externas:



Función:

01 junio 2006

BreakPoints en el Debug (Los menos usados)

Todos utilizamos el debug, pero por lo general no aprovechamos toda su potencia. Ya estamos acostumbrados al Watch de PB, pero rara vez utilizamos las opciones avanzadas de los puntos de interrupción. Cada vez que me toca usarlas, tengo que tirar de la ayuda en html de PB, para recordar como se hacía, y en muchos casos tiramos por la calle de en medio poniendo un IF dentro del bucle que modifica una variable que queremos comprobar cuando alcanzo cierto valor.

Hoy ha tocado, y por eso he querido poner aquí y compartir la sencillez y potencia de las opciones avanzadas de los puntos de interrupción.

Parar cuando queremos parar si una variable cambia de valor:

El punto de interrupción se puede aplicar sobre cualquier variable dentro del alcance. Aquí hay una cosa importante, que es lo que siempre me ha estado confundiendo y es que si estamos en el Painter del Debug pero sin tener este arrancado, solo podemos indicar variables globales, para indicar que se pare cuando una variable local cambie, hay que lanzar el la aplicación en modo debug con una parada dentro del código donde se encuentra la variable local que deseamos utilizar para pausar el debug, y en ese momento ya podemos indicar que variable deseamos que pause el debug cuando esta cambie.

Para poner los puntos de interrupción asociados al cambio de la variable podemos arrastrarla desde el watch o la zona de variables a la zona de BreakPoints, o hacer en dichos sitios click con el botón derecho sobre la variable e indicar “Break on Change”, también en la ventana “Edit BreakPoints” en la ficha de “Variable” podemos añadir una nueva.

Como parar el debug cuando una variable alcance un valor determinado:

El método, creo que más sencillo, es poner un punto de interrupción normal y hacer doble click sobre él en la zona “Breakpoints” y en el cuadro de diálogo que aparece poner la condición deseada, por ej.: li_x = 5, al aceptar, podremos ver en la zona Breakpoints dicha condición del siguiente modo: objeto.evento.linea when li_x = 5.

Además, podemos indicar el número total de ocurrencias que deben darse para realizar una parada, de tal modo que si ponemos un punto de interrupción e indicamos que han de darse 3 ocurrencias, has que no se pase tres veces por ese punto no se pausara el debug, una vez pausado se reinicia el contador interno y sucederá lo mismo si otra vez se llega a 3 veces (según este ejemplo).

Si al introducir el número de ocurrencia lo hacemos junto con una condición no se pausará del debug has que dicha condición se de tres veces.

Lo que falta:

Sería una gozada poder parar el debug cuando una variable cambiara X veces. Cuidado, no hay que confundir esta opción con la de que se pause el debug en un punto determinado del código cuando la variable alcance un valor. Si os fijáis en el cuadro de diálogo “Edit Breakpoints” en la ficha “Variables”, podemos introducir la variable que deseamos que pare el debug cuando cambie, pero sin poder indicarle aquí el número de veces.

04 abril 2006

Evaluación de Traslation Toolkit de PB

Hemos tenido que traducir una aplicación en la empresa, y hemos evaluado tres posibilidades para realizar esta tarea: directamente sobre una copia de los fuentes, mediante una herramienta denominada Enable de "Best In", o mediante PowerBuilder Translation Toolkit, herramienta de todos conocida, por incluirse en el paquete de aplicaciones de PB, pero que pocos han necesitado usar.

Entre las tres opciones, y pensando que incialmente que deberían mantenerse tanto la versión en Español como la de Ingles, y no siendo un requerimiento cambiar de idioma en "Caliente", se decidió usar Translation Toolkit.

Mi opinión es que es una herramienta útil inicialmente, ya que extrae frases de donde uno no se lo espera, como por ejemplo de los checkBox, de los DisplayValue de los DropDawnListBox, etc., pero peca en el tema de los redimensionamientos de las cajas donde aparecen los textos, y el problema no es que no ajuste los tamaños inicialmente, sino que biene despues.

Una vez traducido todo en un diccionario almacenado en una BD, podemos generar el nuevo código con las frases traducidas, y luego nos queda la ardua labor de redimensionar las cajas necesarias... bueno, esto podría se parte del trabajo, pero la putada no es esta, la putada es que al cambiar una frase de un objeto de una librería y pedirle que genere otra vez la versión traducida, te machaca toda la pbl donde se encuentre dicho objeto, por lo que habría que volver a redimensionar todos los controles.

Al final, en nuestro caso, después de la traducción inicial se decidió mantener solo la versión traducida, por lo que ya tocamos directamente sobre el código y nos quitamos de rollos.

La siguiente imagen corresponde con el módulo que permite definir el proyecto y marcar los objetos del mismo que deben traducirse:

Esta otra imagen corresponde con el módulo que permite traducir las frases, lo que esta bien para esto es que dispone de un acceso independiente para acceder solo a este módulo para que un traductor se pueda encargar de esta parte, sin acceso al resto de la herramienta:

Y por último os muestro una pantalla donde se seleccionan los objetos que se quieren traducir, en este caso se seleccionan los objetos para los que hay que cambiar las frases:

17 marzo 2006

Evaluación Visual Expert

Este producto ya lo vi hace tiempo y recientemente he tenido la oportunidad de asistir a una presentación del mismo en mi empresa, ya que se plantean la posibilidad de adquirirlo. Los AP tenemos que dar el visto bueno, y después, como en cualquier gran multinacional, empiezan los trámites burocráticos, hasta conseguir la compra. En principio, los tres AP pensamos que pueden ayudarnos en nuestro trabajo, ya que da mucha facilidad a la hora de localizar objetos dentro de la aplicación, pero ante todo la mayor ventaja es la evaluación de impacto sobre el cambio de cualquier elemento, ya que podemos visualizar de un modo cómodo, el resto de elementos que hacen uso del que nosotros queremos cambiar. Saber usarlo no parece complicado, por lo menos en cuanto a las cosas sencillas y elementales, los detalles complejos supongo que se podrán resolver mediante la ayuda, aunque esta pueda ser algo escasa, o más que escasa, que carezca de suficientes palabras claves para localizar lo que nos interesa, esto lo comento, por que en la misma presentación tuvimos una duda de uso e intente buscar en la ayuda y fue imposible localizar lo que queríamos resolver. También facilita la generación de documentación técnica e informes como por ejemplo variables sin utilizar, pudiendo especificar el alcance de las mismas, y otra serie de filtros, en general este apartado esta bien, lastima que no exista aún aplicaciones que generen la documentación funcional y el manual de usuario. El producto es multilenguaje (francés, ingles y español) y puedes cambiar el idioma en caliente, pero hay cosas que no se traducen, por supuesto la ayuda siempre estará en correcto ingles.En definitiva son muchos más pros que contras y nos puede ayudar bastante, claro esta que hay que conocer sus apellidos, que en este caso, hoy por hoy ronda los 7.000 € e incluye tres licencias, y cuantas más licencias necesites, como es de esperar, más económica resulta cada una de ellas. Si queréis más información la podéis encontrar en la Web de Visual Expert. Este es el aspecto general de la aplicación:




Aun tendremos que realizar una revisión pendiente, por lo que iré actualizando directamente el post, para poner cualquier información que se interesante destacar.

08 marzo 2006

Función para obtener el módulo

Tal vez aquí se podría abrir un tema de discusión, en cuanto a la capacidad de las funciones de sistema de PB para tratar números de precisión doble o real. Muchas de estas funciones, como por ejemplo truncate, round o string (para conversiones de números), solo son capaces de trabajar adecuadamente con variables decimal, aunque la ayuda no lo especifique. Por otro lado, y centrandome en el tema que me ocupa en este post, la función mod, que si parece ser capaz de trabajar con variables de precisión doble, no figurá en el browser/funtions/systemfuntions que este sobrescrita, y por código o en el watch del debug, se puede comprobar que debe existir una definición para doble y otra para long. Además, por razones de precisión cuando debe obtenerse los módulos de números muy grandes, se recomienda utilizar números enteros y no fraccionados o decimales, y el algoritmo que se necesita para dicha operación requiere cortar el número como si se tratase de una cadena, y en base de datos, el número con el que tenemos que trabajar en nuestro caso esta almacenado como cadena, ya que a excepción de la comprobación del dígito de control, no se realiza ninguna otra operación, por esto he creado una función que admite dos parámetros, por un lado un string con el número que del que hay que obtener el módulo y otro argumento con el divisor, en este caso un integer. La función queda así:

DEFINICIÓN:

integer uf_modlong(string as_valor, integer ai_divisor)
CÓDIGO:

06 marzo 2006

Comprobación del dígito de control del IBAN

En este script, partimos de la premisa de disponer del IBAN formateado adecuadamente, así iremos al grano, y dejo que cada cual utilice las comprobaciones que considere oportunas para ver si el IBAN tiene el formato adecuado. Para concretar más el formato utilizado para el ejemplo será AANNCCCCCCCCCCCCCCCCCCCC, donde:

  • AA: Código del país de la entidad (Letras en mayúsculas).
  • NN: Dígito de control del IBAN (Números).
  • CCCCCCCCCCCCCCCCCCCC: Número de cuenta.

En primer lugar convertimos las letras a números, contando que la A vale 10, la B vale 11 y así sucesivamente, de tal modo que BE62510007547061 quedaría 111462510007547061.


ls_letras = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
ls_aux = string(pos(ls_letras, left(as_IBAN, 1))+9)
ls_aux += string(pos(ls_letras, mid(as_IBAN, 2, 1))+9)


En segundo paso consiste en mover los primeros seis caracteres a la derecha de tal modo que 111462510007547061 quedará 510007547061111462:


as_IBAN = mid(as_IBAN, 5) + ls_aux + mid(as_IBAN, 3, 2)


Y para finalizar hay que comprobar si el resto de la división del número obtenido entre 97 es 1. Si el resultado fuese distinto, el dígito de control sería incorrecto.


mod(double(as_IBAN), 97)


En este último punto hay un temita que puede tenerse en cuenta, y es que por razones de precisión se recomienda realizar esta operación con números enteros y no fraccionados o decimales, y como pordrá comprobarse el número con el que hay que operar es mayor que el que se puede almacenar en un unsignedlong (mayor entero en PB), como un entero de 32 bits o 64 bits representa un máximo de 9 ó 18 dígitos, se puede realizar la operación por partes tal y como describo a continuación:

  1. Cálculo del módulo 97 de los primeros 9 dígitos del número.

    Ej: mod(510007547, 97) = 74
  2. Construcción del siguiente entero de 9 dígitos, colocando en primer lugar el resultado del paso anterior. seguido por los siguientes 7/8 dígitos del número y calcular el módulo 97.

    Ej: mod(740611114, 97) = 12.
  3. Repetir el paso dos hasta que todos los dígitos del número hayan sido procesados. Ej: mod(1262, 97) = 1

Script para eliminar caracteres no deseados

El siguiente script permite eliminar caracteres no deseados de un string, en este caso en concreto, elimina todos los que no sean una letra mayúscula o un número, pero si se quiere modificar los caracteres que han de eliminarse, simplemente se debe cambiar el argumento proporcionado a la función match y si fuese necesario jugar con la negación de las sentencias If. DEFINICIÓN:
public string gf_limpiar(string as_texto)
CÓDIGO:

02 marzo 2006

Standards ISO

Para la implementación de un algoritmo de validación del IBAN, que publicaré más adelante, he necesitado localizar la norma ISO que identifica que letras están asociadas con cada país. He encontrado por un lado la "English country names and code elements - ISO 3166 Code lists" publicado por el propio organismo que la implemento, y por otro lado, como siempre días después intentando localizar la página mencionada, he llegado hasta la web ADAGO, concretamente a la lista de standards ISO. Además de aparecer la lista de códigos de países hay un montón de standards interesantes.