31 de mayo de 2002

Número a letras en Inglés (Stored Procedure para SQL)

Este Stored Procedure fue modificado de una rutina de VFP al lenguaje Transact-SQL-

Retorna una cadena en Inglés

Ejemplo:
EXECUTE sp_Num2Word 3245.35
--> THREE THOUSAND TWO HUNDRED FORTY FIVE AND 35/100

/* Stored Procedure */
IF EXISTS (SELECT * FROM sysobjects WHERE id = object_id(N'[dbo].[sp_Num2Word]') AND OBJECTPROPERTY(id, N'IsProcedure') = 1)
  DROP PROCEDURE [dbo].[sp_Num2Word]
GO

SET QUOTED_IDENTIFIER  OFF    SET ANSI_NULLS  ON 
GO

CREATE PROCEDURE sp_Num2Word @Numero NUMERIC(20,2) AS
BEGIN
SET NOCOUNT ON
DECLARE @lnEntero INT, 
  @lcRetorno VARCHAR(512), 
  @lnTerna INT, 
  @lcMiles VARCHAR(512),
  @lcCadena VARCHAR(512),
  @lnUnidades INT, 
  @lnDecenas INT,
  @lnCentenas INT,
  @lnFraccion INT

  SELECT @lnEntero = CAST(@Numero AS INT),
    @lnFraccion = (@Numero - @lnEntero) * 100,
    @lcRetorno = '', 
    @lnTerna = 1

  WHILE @lnEntero > 0
  BEGIN /* WHILE */
    -- Recorro terna por terna
    SELECT @lcCadena = ''
    SELECT @lnUnidades = @lnEntero % 10
    SELECT @lnEntero = CAST(@lnEntero/10 AS INT)
    SELECT @lnDecenas = @lnEntero % 10
    SELECT @lnEntero = CAST(@lnEntero/10 AS INT)
    SELECT @lnCentenas = @lnEntero % 10
    SELECT @lnEntero = CAST(@lnEntero/10 AS INT)

    -- Analizo las unidades
    SELECT @lcCadena =
    CASE /* UNIDADES */
      WHEN @lnUnidades = 1 THEN 'ONE ' + @lcCadena
      WHEN @lnUnidades = 2 THEN 'TWO ' + @lcCadena
      WHEN @lnUnidades = 3 THEN 'THREE ' + @lcCadena
      WHEN @lnUnidades = 4 THEN 'FOUR ' + @lcCadena
      WHEN @lnUnidades = 5 THEN 'FIVE ' + @lcCadena
      WHEN @lnUnidades = 6 THEN 'SIX ' + @lcCadena
      WHEN @lnUnidades = 7 THEN 'SEVEN ' + @lcCadena
      WHEN @lnUnidades = 8 THEN 'EIGHT ' + @lcCadena
      WHEN @lnUnidades = 9 THEN 'NINE ' + @lcCadena
      ELSE @lcCadena
    END /* UNIDADES */

    -- Analizo las decenas
    SELECT @lcCadena =
    CASE /* DECENAS */
      WHEN @lnDecenas = 1 THEN
        CASE @lnUnidades
          WHEN 0 THEN 'TEN '
          WHEN 1 THEN 'ELEVEN '
          WHEN 2 THEN 'TWELVE '
          WHEN 3 THEN 'THIRTEEN '
          WHEN 4 THEN 'FOURTEEN '
          WHEN 5 THEN 'FIFTEEN '
          WHEN 6 THEN 'SIXTEEN '
          WHEN 7 THEN 'SEVENTEEN '
          WHEN 8 THEN 'EIGHTEEN '
          WHEN 9 THEN 'NINETEEN '
        END
      WHEN @lnDecenas = 2 THEN 'TWENTY ' + @lcCadena
      WHEN @lnDecenas = 3 THEN 'THIRTY ' + @lcCadena
      WHEN @lnDecenas = 4 THEN 'FORTY ' + @lcCadena
      WHEN @lnDecenas = 5 THEN 'FIFTY ' + @lcCadena
      WHEN @lnDecenas = 6 THEN 'SIXTY ' + @lcCadena
      WHEN @lnDecenas = 7 THEN 'SEVENTY ' + @lcCadena
      WHEN @lnDecenas = 8 THEN 'EIGHTY ' + @lcCadena
      WHEN @lnDecenas = 9 THEN 'NINETY ' + @lcCadena
      ELSE @lcCadena
    END /* DECENAS */


    -- Analizo las centenas
    SELECT @lcCadena =
    CASE /* CENTENAS */
      WHEN @lnCentenas = 1 THEN 'ONE HUNDRED ' + @lcCadena
      WHEN @lnCentenas = 2 THEN 'TWO HUNDRED ' + @lcCadena
      WHEN @lnCentenas = 3 THEN 'THREE HUNDRED ' + @lcCadena
      WHEN @lnCentenas = 4 THEN 'FOUR HUNDRED ' + @lcCadena
      WHEN @lnCentenas = 5 THEN 'FIVE HUNDRED ' + @lcCadena
      WHEN @lnCentenas = 6 THEN 'SIX HUNDRED ' + @lcCadena
      WHEN @lnCentenas = 7 THEN 'SEVEN HUNDRED ' + @lcCadena
      WHEN @lnCentenas = 8 THEN 'EIGHT HUNDRED ' + @lcCadena
      WHEN @lnCentenas = 9 THEN 'NINE HUNDRED ' + @lcCadena
      ELSE @lcCadena
    END /* CENTENAS */

    -- Analizo la terna
    SELECT @lcCadena = 
    CASE /* TERNA */
      WHEN @lnTerna = 1 THEN @lcCadena
      WHEN @lnTerna = 2 THEN @lcCadena + ' THOUSAND '
      WHEN @lnTerna = 3 THEN @lcCadena + ' MILLON '
      WHEN @lnTerna = 4 THEN @lcCadena + ' BILLON '
      ELSE ''
    END /* TERNA */

    -- Armo el retorno terna a terna
    SELECT @lcRetorno = @lcCadena  + @lcRetorno
    SELECT @lnTerna = @lnTerna + 1

  END /* WHILE */

  IF @lnTerna = 1  
    SELECT @lcRetorno = 'ZERO'

  SELECT RTRIM(@lcRetorno) + ' AND ' + LTRIM(STR(@lnFraccion,2)) + '/100'

END
GO
SET QUOTED_IDENTIFIER  OFF
SET ANSI_NULLS  ON 
GO

Luis María Guayán

21 de mayo de 2002

Número a Letras (Stored Procedure para SQL)

Este Stored Procedure fue modificado de una rutina de VFP al lenguaje Transact-SQL
Retorna una cadena en Español

Ejemplo:
EXECUTE sp_Num2Let 3245.35
--> TRES MIL DOSCIENTOS CUARENTA Y CINCO CON 35/100

/* Stored Procedure */
IF EXISTS (SELECT * FROM sysobjects WHERE id = object_id(N'[dbo].[sp_Num2Let]') AND OBJECTPROPERTY(id, N'IsProcedure') = 1)
  DROP PROCEDURE [dbo].[sp_Num2Let]
GO

SET QUOTED_IDENTIFIER  OFF    SET ANSI_NULLS  ON 
GO

CREATE PROCEDURE sp_Num2Let @Numero NUMERIC(20,2) AS
BEGIN
SET NOCOUNT ON
DECLARE @lnEntero INT, 
  @lcRetorno VARCHAR(512), 
  @lnTerna INT, 
  @lcMiles VARCHAR(512),
  @lcCadena VARCHAR(512),
  @lnUnidades INT, 
  @lnDecenas INT,
  @lnCentenas INT,
  @lnFraccion INT

  SELECT @lnEntero = CAST(@Numero AS INT),
    @lnFraccion = (@Numero - @lnEntero) * 100,
    @lcRetorno = '', 
    @lnTerna = 1

  WHILE @lnEntero > 0
  BEGIN /* WHILE */
    -- Recorro terna por terna
    SELECT @lcCadena = ''
    SELECT @lnUnidades = @lnEntero % 10
    SELECT @lnEntero = CAST(@lnEntero/10 AS INT)
    SELECT @lnDecenas = @lnEntero % 10
    SELECT @lnEntero = CAST(@lnEntero/10 AS INT)
    SELECT @lnCentenas = @lnEntero % 10
    SELECT @lnEntero = CAST(@lnEntero/10 AS INT)

    -- Analizo las unidades
    SELECT @lcCadena =
    CASE /* UNIDADES */
      WHEN @lnUnidades = 1 AND @lnTerna = 1 THEN 'UNO ' + @lcCadena
      WHEN @lnUnidades = 1 AND @lnTerna <> 1 THEN 'UN ' + @lcCadena
      WHEN @lnUnidades = 2 THEN 'DOS ' + @lcCadena
      WHEN @lnUnidades = 3 THEN 'TRES ' + @lcCadena
      WHEN @lnUnidades = 4 THEN 'CUATRO ' + @lcCadena
      WHEN @lnUnidades = 5 THEN 'CINCO ' + @lcCadena
      WHEN @lnUnidades = 6 THEN 'SEIS ' + @lcCadena
      WHEN @lnUnidades = 7 THEN 'SIETE ' + @lcCadena
      WHEN @lnUnidades = 8 THEN 'OCHO ' + @lcCadena
      WHEN @lnUnidades = 9 THEN 'NUEVE ' + @lcCadena
      ELSE @lcCadena
    END /* UNIDADES */

    -- Analizo las decenas
    SELECT @lcCadena =
    CASE /* DECENAS */
      WHEN @lnDecenas = 1 THEN
        CASE @lnUnidades
          WHEN 0 THEN 'DIEZ '
          WHEN 1 THEN 'ONCE '
          WHEN 2 THEN 'DOCE '
          WHEN 3 THEN 'TRECE '
          WHEN 4 THEN 'CATORCE '
          WHEN 5 THEN 'QUINCE '
          ELSE 'DIECI' + @lcCadena
        END
      WHEN @lnDecenas = 2 AND @lnUnidades = 0 THEN 'VEINTE ' + @lcCadena
      WHEN @lnDecenas = 2 AND @lnUnidades <> 0 THEN 'VEINTI' + @lcCadena
      WHEN @lnDecenas = 3 AND @lnUnidades = 0 THEN 'TREINTA ' + @lcCadena
      WHEN @lnDecenas = 3 AND @lnUnidades <> 0 THEN 'TREINTA Y ' + @lcCadena
      WHEN @lnDecenas = 4 AND @lnUnidades = 0 THEN 'CUARENTA ' + @lcCadena
      WHEN @lnDecenas = 4 AND @lnUnidades <> 0 THEN 'CUARENTA Y ' + @lcCadena
      WHEN @lnDecenas = 5 AND @lnUnidades = 0 THEN 'CINCUENTA ' + @lcCadena
      WHEN @lnDecenas = 5 AND @lnUnidades <> 0 THEN 'CINCUENTA Y ' + @lcCadena
      WHEN @lnDecenas = 6 AND @lnUnidades = 0 THEN 'SESENTA ' + @lcCadena
      WHEN @lnDecenas = 6 AND @lnUnidades <> 0 THEN 'SESENTA Y ' + @lcCadena
      WHEN @lnDecenas = 7 AND @lnUnidades = 0 THEN 'SETENTA ' + @lcCadena
      WHEN @lnDecenas = 7 AND @lnUnidades <> 0 THEN 'SETENTA Y ' + @lcCadena
      WHEN @lnDecenas = 8 AND @lnUnidades = 0 THEN 'OCHENTA ' + @lcCadena
      WHEN @lnDecenas = 8 AND @lnUnidades <> 0 THEN 'OCHENTA Y ' + @lcCadena
      WHEN @lnDecenas = 9 AND @lnUnidades = 0 THEN 'NOVENTA ' + @lcCadena
      WHEN @lnDecenas = 9 AND @lnUnidades <> 0 THEN 'NOVENTA Y ' + @lcCadena
      ELSE @lcCadena
    END /* DECENAS */


    -- Analizo las centenas
    SELECT @lcCadena =
    CASE /* CENTENAS */
      WHEN @lnCentenas = 1 AND @lnUnidades = 0 AND @lnDecenas = 0 THEN 'CIEN ' + @lcCadena
      WHEN @lnCentenas = 1 AND NOT(@lnUnidades = 0 AND @lnDecenas = 0) THEN 'CIENTO ' + @lcCadena
      WHEN @lnCentenas = 2 THEN 'DOSCIENTOS ' + @lcCadena
      WHEN @lnCentenas = 3 THEN 'TRESCIENTOS ' + @lcCadena
      WHEN @lnCentenas = 4 THEN 'CUATROCIENTOS ' + @lcCadena
      WHEN @lnCentenas = 5 THEN 'QUINIENTOS ' + @lcCadena
      WHEN @lnCentenas = 6 THEN 'SEISCIENTOS ' + @lcCadena
      WHEN @lnCentenas = 7 THEN 'SETECIENTOS ' + @lcCadena
      WHEN @lnCentenas = 8 THEN 'OCHOCIENTOS ' + @lcCadena
      WHEN @lnCentenas = 9 THEN 'NOVECIENTOS ' + @lcCadena
      ELSE @lcCadena
    END /* CENTENAS */

    -- Analizo la terna
    SELECT @lcCadena = 
    CASE /* TERNA */
      WHEN @lnTerna = 1 THEN @lcCadena
      WHEN @lnTerna = 2 AND (@lnUnidades + @lnDecenas + @lnCentenas <> 0) THEN @lcCadena + ' MIL '
      WHEN @lnTerna = 3 AND (@lnUnidades + @lnDecenas + @lnCentenas <> 0) AND 
        @lnUnidades = 1 AND @lnDecenas = 0 AND @lnCentenas = 0 THEN @lcCadena + ' MILLON '
      WHEN @lnTerna = 3 AND (@lnUnidades + @lnDecenas + @lnCentenas <> 0) AND
        NOT (@lnUnidades = 1 AND @lnDecenas = 0 AND @lnCentenas = 0) THEN @lcCadena + ' MILLONES '
      WHEN @lnTerna = 4 AND (@lnUnidades + @lnDecenas + @lnCentenas <> 0) THEN @lcCadena + ' MIL MILLONES '
      ELSE ''
    END /* TERNA */

    -- Armo el retorno terna a terna
    SELECT @lcRetorno = @lcCadena  + @lcRetorno
    SELECT @lnTerna = @lnTerna + 1

  END /* WHILE */

  IF @lnTerna = 1  
    SELECT @lcRetorno = 'CERO'

  SELECT RTRIM(@lcRetorno) + ' CON ' + LTRIM(STR(@lnFraccion,2)) + '/100'

END
GO
SET QUOTED_IDENTIFIER  OFF
SET ANSI_NULLS  ON 
GO
Luis María Guayán

11 de mayo de 2002

ADO para desarrolladores Visual FoxPro

Artículo escrito por Antonio Castaño, Microsoft Visual FoxPro - MVP y publicado en la revista del MUG (MS Users Group de Argentina) en el mes de Junio de 2000.
Nota del Editor:
Este artículo fue escrito por Antonio Castaño, Microsoft Visual FoxPro - MVP y publicado en la revista del MUG (MS Users Group de Argentina) y publicado en el mes de Junio de 2000. Cabe aclarar que algunos temas y vínculos pueden estar desactualizados.
Introducción

De todas las tecnologías que han surgido en los últimos tiempos, ADO es una de las más interesantes y una de las que se utiliza en mayor cantidad de productos y herramientas de la familia Microsoft.
Y como se trata de una tecnología específica de acceso a datos, es de especial interés para los desarrolladores Visual FoxPro.

El objetivo de este artículo es destacar algunos características de especial interés para quienes usamos Visual FoxPro como principal herramienta, y comentar algunas particularidades de su uso. No se pretende en este corto espacio, desarrollar todas las características y funcionalidades de ADO. Existe una gran cantidad de documentación, artículos y libros sobre la materia, algunos de los cuales se mencionan en el título Referencias.

Algunas definiciones

La tecnología ADO (Active Data Objects) forma parte de la estrategia definida por Microsoft para acceso a datos (UDA: Universal Data Access), que se implementa a través del sucesor de ODBC, OLE DB (más información: http://www.microsoft.com/data).

ADO es un conjunto de clases que se instalan en cualquiera de las versiones de Windows de 32 bits, y sus objetivos son:
  • Proveer acceso a datos independientemente de su formato y/o ubicación
  • Proveer una interfaz basada en objetos a OLE DB, a través de un modelo de objetos simple y flexible
  • Que pueda ser usado desde cualquier lenguaje / entorno que permita trabaja con objetos COM/ActiveX
ADO y Visual FoxPro

En su versión actual, VFP no cuenta con mecanismos específicos de RAD (Rapid Application Developement) para hacer uso de ADO, pero al ser ADO un conjunto de clases COM, pueden ser utilizado en su funcionalidad completa sin ningún problema.

Si bien otras herramientas de Visual Studio (VB, VI) cuentan con algunos componentes RAD específicos para hacer uso de la funcionalidad de ADO, los desarrolladores de cualquiera herramienta que deban encarar la tarea de hacer código robusto y realmente reutilizable, deberán recurrir al uso programático de ADO, tal cual se hace en Visual FoxPro.

Visual FoxPro tiene la natural características de ser una herramienta ideal para construir aplicaciones basadas en componentes, gracias a su rica implementación de programación orientada a objetos (OOP).

Es por eso que para los desarrolladores VFP, ADO, al ser una tecnología basada en objetos, abre todo un mundo de posibilidades. Uno de los fuertes de Visual FoxPro, dentro del espectro de herramientas de Visual Studio, es el desarrollo de componentes de negocio en los que sea crítica la habilidad para obtener y manipular datos. La posibilidad de utilizar esta tecnología para el intercambio de información entre estos componentes, ofrece nuevas alternativas para implementar soluciones robustas y flexibles.

Por otra parte, cabe destacar que, al haber sido ADO desarrollado por integrantes del equipo que desarrolló Visual FoxPro, con el objeto de aprovechar la excepcional tecnología de manejo de cursores que tiene VFP, existen una cantidad de conceptos y hasta de terminología, que resultarán familiares.

Manos a la obra...

Para trabajar con ADO, es importante instalar el Service Pack 3 de Visual Studio. Este SP, además de solucionar una lista importante de bugs, mejora los mecanismo para las comunicaciones a través de COM. Por ejemplo, con la aplicación de este SP, VFP es un “mejor ciudadano” corriendo bajo MTS (Microsoft Transaction Server), y permite construir multithreaded dlls. Otro de los efectos visibles de estas mejoras, es que teniendo aplicado el SP3 es posible utilizar controles ActiveX que permitan su asociación (“boundeo”) a recordsets de ADO (ej: Microsoft Hierarchical Flex Grid, o Protoview TreeViexX).

Otro componente que es conveniente instalar para trabajar con ADO, es una .dll que se publicó con posterioridad a la salida de la versión 6, pero que no está incluida en el Service Pack 3. Se trate de vfpcom.dll. Esta .dll, junto con la documentación de cómo usarla y algunos ejemplos, puede obtenerse en el sitio de MS: http://msdn.microsoft.com/vfoxpro/downloads/vfpcom.exe

Esta .dll es un COM Server e incluye una única clase, comutil, que tiene una serie de métodos que proveen dos tipos de funcionalidades bien diferentes, pero que ambos pueden ser de utilidad cuando se trabaja con ADO.

Por una parte, están los métodos RsToCursor y CursorToRs. Cómo su nombre hace suponer, estos métodos permiten convertir RecordSets de ADO a cursores de VFP y viceversa. Ya existían unas funciones para realizar estas tareas, desarrolladas por Ken Levy y puestas en el dominio público (pueden obtenerse en el sitio de Ken en: http://www.classx.com), pero estas .dll realizan la conversión en forma mucho más eficiente. Es necesario aclarar que esta .dll no es soportada por MS salvo en lo que hace a su instalación; dada las constantes actualizaciones de ADO, existe alguna probabilidad de que con el tiempo aparezca algún tipo de incompatibilidad. De hecho, nuestra experiencia es que hay ciertos tipos de datos en RecordSets ADO que la .dll no soporta.
De todas maneras, como lo indican las buenas prácticas de programación, sea cual sea el mecanismo que se utilice para hacer esta conversión, deberá estar convenientemente encapsulado y parametrizado para poder, eventualmente, utilizar un método u otro.

El otro grupo de funcionalidades que provee la vfpcom.dll se refiere a la posibilidad de detectar eventos de objetos COM. Esta funcionalidad no está presente en VFP 6 (si bien ya está anunciado que va a formar parte de funcionalidad básica de VFP 7).

Una de las particularidades de ADO, es su capacidad de generar eventos. Esta .dll permite detectar la presencia de esos eventos desde un aplicación VFP. Sin embargo, la utilidad de estos métodos, va mucho más allá del uso de ADO. Permitiría, por ejemplo, hacer una aplicación VFP que utilice MSMQ (Microsoft Messaging Queue Server), y detecte los eventos asociados a las colas de mensajes. O bien una aplicación de automatización de envío y recepción de emails, que responda a eventos de Outlook.

Esta funcionalidad se implemente a través de 3 métodos de la clase comutil, que son:
  • ExportEvents
  • BindEvents
  • UnBindEvents
El método ExportEvents es para ser usado en “tiempo de desarrollo”. Permite exportar la interfaz del objeto COM (es decir, qué eventos genera) a una clase VFP. La clase VFP se genera en un archivo de texto (es decir, la definición está hecha en forma programática - no visual -, con DEFINE CLASS .... ENDDEFINE).

A partir de esa “cáscara”, se puede desarrollar una clase, que tenga los métodos correspondientes para “atender” a los eventos de la clase COM.

En tiempo de ejecución, a través del método BindEvents se realiza la asociación entre el objeto COM y un objeto de dicha clase VFP. Las siguientes líneas de código muestran cómo asociar una clase VFP a un objeto COM, en este caso, de la clase ADODB.Connection:

oUtil = createobject('vfpcom.comutil')
oConn = createobject('ADODB.Connection')
* ADOConnEvents es una clase VFP,
* desarrollada con la ayuda del método ExportEvents
oMonitorADOConn = newobject('ADOConnEvents','MisClases.prg')
oUtil.BindEvents(oConn, oMonitorADOConn)

A partir de ese momento, cuando el objeto Connection (oConn) genere un evento, se invocará el método correspondiente de la clase ADOConnEvents.

¿Para qué usar algo nuevo?

Ahora bien: supongamos que nos tomamos el tiempo de estudiar y entender el modelo de objetos de ADO y el uso de sus propiedades y métodos, y aprendemos a usarlo desde Visual FoxPro. La pregunta que queda por hacernos es: ¿cuáles son las razones para utilizar un mecanismo nuevo ?. Visual FoxPro, a través de los mecanismo de Vistas Remotas y de SQLPassThrough ofrece mecanismos más que suficientes para desarrollar una aplicación robusta y de buen desempeño.

Una primera razón sería la de poder utilizar las características y posibilidades de OLE DB para acceder a una fuente de datos para la cual dispongamos del correspondiente OLE DB Provider, por ejemplo, a Microsoft SQL Server. Características dentro de las cuales se pueden incluir cuestiones de performance o el uso de nuevas funcionalidades, como ser la obtención de Recordsets jerárquicos (o Data Shapes) a través del uso del provider MSDataShape y la instrucción SHAPE, disponibles si utilizamos ADO.

Una segunda razón podría ser, simplemente, el querer utilizar una tecnología más moderna y con más potencial en cuanto a su evolución futura, de manera de prepararse mejor para la próxima generación de herramientas de desarrollo.

Una tercera razón, quizás la de mayor importancia, es si nos encontramos ante la necesidad de desarrollar una función o método de un componente o clase, al cual se debe enviar, o del cual se deba recibir, un conjunto de datos, es decir, una serie de registros del mismo tipo, cuya cantidad pueda ser variable (por ejemplo, las líneas de detalle de un Pedido). Si se trata de una función o método que va a ser utilizada por otro componente también desarrollado en Visual FoxPro, la solución a esta necesidad, podría resolverse mediante el uso de cursores nativos de Fox. Pero si es necesario que dicho componente sea implementado como una clase COM, entonces es que se hace necesario buscar otra alternativa. Existe la posibilidad de utilizar vectores o conjuntos de caracteres concatenados, o hasta la posibilidad de utilizar XML. En todos estos casos, se deberán desarrollar mecanismos de preparación (codificación o armado) y recepción (de-codificación o parseo) de los datos, que inevitablemente resultarán en código extenso y, probablemente, difícil de mantener. La alternativa de utilizar ADO, ofrece un mecanismo simple y elegante para satisfacer este necesidad. Es decir, el intercambio de información entre el componente desarrollado en Visual FoxPro y sus clientes, se implementará a través del envío y recepción de RecorSets ADO.

Conclusiones

Si bien la decisión de qué mecanismos utilizar en cada caso particular, dependerá del tipo de aplicación y de las necesidades de funcionamiento y performance que se definan, así como de la posible evolución de una determinada aplicación, nos parece que, dada las características del uso de ADO en la presente versión de Visual FoxPro, es recomendable utilizarlo solamente en aquellos casos en que encontremos un beneficio real. Como se dijo, los mecanismo nativos disponibles, ofrecen un grado de productividad mayor, a la vez que un resultado en términos de calidad de la aplicación más que suficientes.

También es cierto, como se dijo, que probablemente las próximas versiones de Visual FoxPro, ofrezcan una mayor integración y permitan aprovechar esta tecnología, de una manera más productiva y eficiente.

Referencias
  • Libros
    • Programming Ado - David Sceppa - Microsoft Press - ISBN: 0735607648
    • PROFESIONAL ADO 2.5 Programming – David Sussman, James Conard, Brian Matsik – Wrox Press – ISBN 1-861002-75-0

5 de mayo de 2002

Colocar una imagen de fondo centrada a un formulario

Colocar una imagen de fondo centrada a un formulario.
_screen.addobject("oImg", "image")
_screen.oImg.picture = "nombre_del_archivo"
_screen.oImg.stretch = 2
_screen.oImg.width = 500
_screen.oImg.visible = .T.
Coloquen esto en el metodo Init del Formulario.
_screen.oImg.left = (_screen.Width/2) - (_screen.oImg.width/2)
_screen.oImg.Top = (_screen.Height/2) - (_screen.oImg.Height/2)
Se debe colocar estos parametros dentro del metodo Refresh del formulario, de manera que no importa el tamaño que tenga la ventana del formulario, la imagen siempre estará en el centro de la misma

4 de mayo de 2002

Números a Letras SQL Server 7/2000

Quienes deseen imprimir documentos en cristal reports desde una base de datos SQL Server 7.0 o 2000, y deseen expresar números en letras en español utilizando un procedimiento almacenado desde el servidor, aqui esta la solución.

Sintaxis para ejecutarlo
exec sp_numero_a_letras '15225.3 ','quetzales',@letras output
El procedimiento necesita de 3 parámetros todos caracteres el primero, es el valor numerico a convertir (obligatorio), el segundo es el parámetro de moneda que se colocara al final del valor (opcional, puede enviarse cadena vacia) y el tercero es la variable, que podran pegar a su SQL donde obtendran el valor de el número enviado.

Espero les sirva.

Fernando España

CREATE procedure sp_numero_a_letras
 @monto numeric(14,2),
 @moneda char(10),
 @letras char(255) output as
begin
declare @unidades char(255),
 @decenas char(255),
 @centenas char(255),
 @especiales char(108),
 @decimales char(25),
 @valor_entero char(9),
 @longitud int,
 @caracteres char(3), 
 @contador int,
 @posicion int,
 @flag  int,
 @decimal int
set nocount on
select @unidades = "un    dos   tres  cuatro" +
    "cinco seis  siete ocho  " +
    "nueve "
select @especiales = "once        doce        trece       "  +
    "catorce     quince      diez y seis "  +
    "diez y sietediez y ocho diez y nueve"
select @decenas = "diez     veinte   treinta  cuarenta " +
    "cincuentasesenta  setenta  ochenta  " +
    "noventa  "
select @centenas = "ciento       doscientos   trescientos  cuatrocientos" +
    "quinientos   seiscientos  setecientos  ochocientos  " +
    "novecientos  "
select @decimal = (@monto - cast(@monto as int)) * 100
select @monto  = round(@monto,0,1)
select @longitud  = LEN( rtrim(cast(cast(@monto as int) as char)))
select @valor_entero = rtrim(cast(cast(@monto as int) as char))
select @valor_entero = replicate("0",9-@longitud)+ substring(@valor_entero,1,@longitud)
select @contador = 1,
 @letras  = replicate(' ',255)
while @contador < 8
 begin /* 0 */
 select @caracteres = substring(@valor_entero,@contador,3)
 if @caracteres <> '000'
  begin /* 1 */
  if substring(@caracteres,1,1) <> '0'
   -- CENTENAS
   begin /* 2 */
   select @posicion = cast(substring(@caracteres,1,1) as int)
   if  @posicion = '1' and
    cast(substring(@caracteres,2,2) as int) = 0
    begin /* 3 */
    
    select @letras = rtrim(@letras) +
       " Cien "
    end /* 3 */
   else
    begin /* 4 */
    select @letras = rtrim(@letras)      +
       " "       +
       substring(@centenas, 13 * (@posicion - 1) + 1,13)
    end /* 4 */
   end /* 2 */
  select @flag = 0
  if cast(substring(@caracteres,2,2) as int) > 10 and
   cast(substring(@caracteres,2,2) as int) < 20
   -- ESPECIALES
   begin /* 5 */
   select @posicion = cast(substring(@caracteres,3,1) as int)
   select @letras = rtrim(@letras)      +
      " "       +
      substring(@especiales, 12 * (@posicion - 1) + 1,12)
   select @flag = 1
   end /* 5 */
  if @flag = 0
   -- DECENAS
   begin /* 6 */
   if substring(@caracteres,2,1) <> '0'
    begin /* 7 */
    select @posicion = cast(substring(@caracteres,2,1) as int)
    if @posicion <> 2 or
     substring(@caracteres,3,1) = '0'
     begin /* 8 */
     select @letras = rtrim(@letras)      +
        " "       +
        substring(@decenas, 9 * (@posicion - 1) + 1,9)
     
     end /* 8 */
    else
     begin /* 9 */
     
     select @letras = rtrim(@letras)      +
        " veinti"
     end /* 9 */
    
    end /* 7 */
   
   if substring(@caracteres,3,1) <> '0'
 
    -- UNIDADES
    begin /* 10 */
    select @posicion = cast(substring(@caracteres,3,1) as int)
    if substring(@caracteres,2,1) <> '0' and
     substring(@caracteres,3,1) <> '0'
     begin /* 11 */
     if substring(@caracteres,2,1) = '2'
      select @letras = rtrim(@letras)     +
         substring(@unidades, 6 * (@posicion - 1) + 1,6)
     else
      select @letras = rtrim(@letras)     +
         " y "      +
         substring(@unidades, 6 * (@posicion - 1) + 1,6)
     end /* 11 */
    
    else
     select @letras = rtrim(@letras)      +
        " "       +
        substring(@unidades, 6 * (@posicion - 1) + 1,6)
    end /* 10 */
 
   end /* 6 */
  if @contador = 1
   begin /* 12 */ 
   if @posicion   = 1 and
    substring(@caracteres,1,2) = '00'
    begin /* 13 */
    select @letras = rtrim(@letras) +
       " millón "
    end /* 13 */
   else
 
--    if @posicion = 1
     begin /* 14 */
 
     select @letras = rtrim(@letras) +
        " millones "
     end /* 14 */
   end /* 12 */
  else
   begin /* 15 */
   if @contador = 4
    begin /* 16 */
    select @letras = rtrim(@letras) +
       " mil "
    end /* 16 */
   end /* 15 */
  end /* 1 */
 select @contador = @contador + 3
  
 end /* 0 */
-- CIENTOS
if right(rtrim(@letras),6) = 'ciento'
 begin /* 17 */
 select @letras = substring(@letras,1,len(rtrim(@letras))-6) +
    "cien "
 end /* 17 */
-- DECIMALES
if @decimal > 0
 select @decimales = "  " + @moneda + " con "        +
     replicate ('0' ,2 - len(rtrim(ltrim(cast (@decimal as char(2)))))) +
     ltrim(rtrim(cast (@decimal as char(2))))   +
     "/100"
else
 select @decimales = "  " + @moneda + " exactos"
-- FINAL
select @letras  = '** '    +
    rtrim(substring(@letras,1,255)) +
    rtrim(@decimales)  +
    ' **'
select "letras" = UPPER(@letras)
end

Cualquier duda, enviar mail a fespana@intelnet.net.gt