viernes, 9 de mayo de 2014

Convertir un número en texto (125,700.63 = ciento veinticinco mil setencientos con 63/100)

Hola a todos. A veces queremos traducir un numero a texto. Esto es util cuando imprimirmos algun recibo o factura... o para la impresion de cheques.


Antes que nada, quiero dejar claro que ésta rutina la tome hace un par de años del foro de todoexpertos.com... aunque no recuerdo de que experto, creo que era Don Javier Cortez, gran conocedor de VFP... maestro! ... pero todos los créditos son para esa persona que adecuó dicha rutina, no es de mi autoría directamente pero es magnifica y sencilla! :) :) :)


Va así:


1 - Crea una rutina .prg llamada 'NToLetra.prg' (sin comillas). En ella, copia el siguiente código:



*** Regresa numero en letras
Procedure NToLetra
Parameter numero
num_car = Str(numero,15,2)
num_dig = Subs(num_car,14,2)
pos = 1
Store "" To num_car_fin,leyenda
For t=1 To 4
Store 0 To uni,dec,cen
cen = Val(Subs(num_car,pos+0,1))
dec = Val(Subs(num_car,pos+1,1))
uni = Val(Subs(num_car,pos+2,1))
pos = pos + 3
letra3 = centena(uni,dec,cen)
letra2 = decenas(uni,dec,cen)
letra1 = unidads(uni,dec,cen)
Do Case
Case t=1
leyenda = IIf(uni+dec+cen=1,"BILLÓN ",IIf(uni+dec+cen>1,"BILLONES ",""))
Case t=2
leyenda = IIf(uni+dec+cen=1,"MILLÓN ",IIf(uni+dec+cen>1,"MILLONES ",""))
Case t=3
leyenda = IIf(uni+dec+cen=1,"MIL ",IIf(uni+dec+cen>1,"MIL ",""))
Case t=4
leyenda = IIf(uni+dec+cen=1,"",IIf(uni+dec+cen>1,"",""))
EndCase
num_car_fin = num_car_fin + letra3 + letra2 + letra1 + leyenda
EndFor
num_1 = Val(Subs(num_car,1,12))
num_2 = Val(Subs(num_car,4,9))
num_3 = Val(Subs(num_car,7,6))
leyenda = ""
If num_1=1
leyenda = "PESO"
Else
If num_2=0 .Or. num_3=0
leyenda = "DE PESOS"
Else
leyenda = "PESOS"
EndIf
EndIf
If num_1 = 0
num_car_fin = "CERO "
leyenda = "PESOS"
EndIf
num_car_fin = num_car_fin + leyenda + ' CON ' + num_dig + "/100"
Return num_car_fin
** Unidades
Procedure unidads
Parameter uni,dec,cen
Do Case
Case uni = 1 .And. dec#1
ctexto = "UN "
Case uni = 2 .And. dec#1
ctexto = "DOS "
Case uni = 3 .And. dec#1
ctexto = "TRES "
Case uni = 4 .And. dec#1
ctexto = "CUATRO "
Case uni = 5 .And. dec#1
ctexto = "CINCO "
Case uni = 6
ctexto = "SEIS "
Case uni = 7
ctexto = "SIETE "
Case uni = 8
ctexto = "OCHO "
Case uni = 9
ctexto = "NUEVE "
OtherWise
ctexto = ""
EndCase
Return ctexto
** Centenas
Procedure centena
Parameter uni,dec,cen
Do Case
Case cen=1 .And. (dec=0 .And. uni=0)
ctexto = "Cien "
Case cen=1 .And. (dec>0 .Or. uni>0)
ctexto = "CIENTO "
Case cen=2
ctexto = "DOSCIENTOS "
Case cen=3
ctexto = "TRESCIENTOS "
Case cen=4
ctexto = "CUATROCIENTOS "
Case cen=5
ctexto = "QUINIENTOS "
Case cen=6
ctexto = "SEISCIENTOS "
Case cen=7
ctexto = "SETECIENTOS "
Case cen=8
ctexto = "OCHOCIENTOS "
Case cen=9
ctexto = "NOVECIENTOS "
OtherWise
ctexto = ""
EndCase
Return ctexto
** Decenas
Procedure decenas
Parameter uni,dec,cen
Do Case
Case dec=1 .and. uni=0
ctexto = "DIEZ "
Case dec=1 .and. uni=1
ctexto = "ONCE "
Case dec=1 .and. uni=2
ctexto = "DOCE "
Case dec=1 .and. uni=3
ctexto = "TRECE "
Case dec=1 .and. uni=4
ctexto = "CATORCE "
Case dec=1 .and. uni=5
ctexto = "QUINCE "
Case dec=1 .and. (uni>5 .and. uni<10)
ctexto = "DIECI"
Case dec=2 .and. uni=0
ctexto = "VEINTE "
Case dec=2 .and. uni>0
ctexto = "VEINTI"
Case dec=3 .and. uni=0
ctexto = "TREINTA "
Case dec=3 .and. uni>0
ctexto = "TREINTA Y "
Case dec=4 .and. uni=0
ctexto = "CUARENTA "
Case dec=4 .and. uni>0
ctexto = "CUARENTA Y "
Case dec=5 .and. uni=0
ctexto = "CINCUENTA "
Case dec=5 .and. uni>0
ctexto = "CINCUENTA Y "
Case dec=6 .and. uni=0
ctexto = "SESENTA "
Case dec=6 .and. uni>0
ctexto = "SESENTA Y "
Case dec=7 .and. uni=0
ctexto = "SETENTA "
Case dec=7 .and. uni>0
ctexto = "SETENTA Y "
Case dec=8 .and. uni=0
ctexto = "OCHENTA "
Case dec=8 .and. uni>0
ctexto = "OCHENTA Y "
Case dec=9 .and. uni=0
ctexto = "NOVENTA "
Case dec=9 .and. uni>0
ctexto = "NOVENTA Y "
OtherWise
ctexto = ""
EndCase
Return ctexto
**--//



2 - En el formulario donde vas a presentar el resultado, coloca esta linea en el evento INIT() del form. (no se que diablos le pasa a esta maldita pagina pero cuando quieres borrar algo con backspace el cursor de escritura se pierde al final del carajo! jajajajajaja! )


SET PROCEDURE TO NToLetra.prg ADDITIVE


3 - Para efectos de ejemplo, pruébalo así como te diré, luego vos lo adecuas a algún evento o método espcifico. Inserta en el form 2 textos y 1 botón. Un text1 en donde cargaras la cantidad y un text2 donde aparecerá la cantidad en letras. En el evento click del botón, coloca un código como éste:


**--Monto a letras
THISFORM.Text2.Value = NToLetra(THISFORM.Text1.Value)
**--//
THISFORM.Refresh


**---



Ahora, solo guarda y ejecutá. Ingresa algún numero en el text1 y luego da click en el botón. La cantidad en letras debe aparecer!


Cualquier problema, duda o sugerencia... comenten! Todo para mejorar. Saludos!


Desde Managua, capital de Nicaragua!
Ravenn :D :D :D

Invertir el orden de los caracteres en una cadena - HOLA = ALOH

Hola. A veces necesitamos hacer cosas un poco fuera de lo normal, pero resulta muy interesante. ::)


Imaginemos que necesitamos invertir una cadena... por ejemplo, para validar algun tipo de numero serial, o algo asi (crear un artificio). Si tenemos la cadena HOLA y queremos invertir las letras para que queden ALOH... la forma de proceder es asi:


LOCAL lcRetValue, lnCounter, lcCadena

**--En éste caso, la cadena es "HOLA"
lcCadena = "HOLA"

**--
lcRetValue = ""

**--Iniciamos bucle
FOR lnCounter = LEN(lcCadena) TO 1 STEP -1

    lcRetValue = lcRetValue + SUBSTR(lcCadena, lnCounter, 1)


ENDFOR
**--//

**--Mostramos datos
MESSAGEBOX(lcRetValue)


Prueben. Cualquier duda o sugerencia para mejorar... es siempre bienvenida.


Un abrazo a todos.
Ravenn!
Desde Managua, Nicaragua!

Cómo evitar CONTROLES VACÍOS (TextBox, ComboBox, EditBox) en un formulario cuando son Obligatorios

Hola a todos. A veces necesitamos validar que, al momento de guardar un registro, algun control como un textbox, editbox, combobox no esten vacios porque es obligatorio que tengan al menos un dato. Por ejemplo: en una factura es incorrecto que el registro se guarde (al facturar un producto) sin el numero de factura. En un catalogo de productos... es incorrecto que se guarde un dato en blanco en la tabla para el campo nombreproducto.

Las validaciones son importantes para asegurar la integridad de los datos que se van a guardar en la base de datos, sea ésta la nativa de foxpro (.dbc) o si usamos otro motor de dase de datos compatible (FIREBIRD, SQL, etc..). Dichas validaciones deben realizarse SIEMPRE en la aplicacion, en el formulario desde el cual se originan. Es incorrecto que un dato de un campo vaya sin validar a la base de datos. ES INCORRECTO Y UNA MALA PRÁCTICA Y FILOSOFIA DE PROGRAMACIÓN.

Por tanto, requerimos en ocasiones... identificar aquellos controles por medio de los cuales ingresamos los datos para guardarlos en las tablas. Lo mas conveniente es colocar un codigo de validación que dispare un STOP o detenimiento del proceso de guardado antes que éste se consuma, es decir, se deben verificar que los datos que consideremos como OBLIGATORIOS o los mas IMPORTANTES sean cargados (digitados) correctamente en los controles antes del correr un proceso de insertado en las tablas.

Para tal fin, he aqui un pequeño código que ha resultado muy satisfactorio. Aclaro que éste no es de mi autoria. Recuerdo que alguien mas lo publico hace años en un foro, cuando yo recien aprendia. Lo tome, lo analice y lo usé. Ahora es un metodo de los mas comunes y usados en mis aplicaciones. Todo el crédito para el autor original, en dado caso si algún dia esa persona lee este post.

1 - Creamos un nuevo metodo en nuestro formulario en modo de diseño(Click en el menu de foxpro, click en el menu form, click en new method) con el nombre validarvacios. Luego, vamos a la pestaña methods de las propiedades del form y buscamos el metodo nuevo. Debe aparecer al final, en letras rojitas. Damos doble click y en el escribimos este codigo:

LOCAL lnObjecto, lcNombre, lctooltiptext, lcContenido, lcFoco


**--Evitar vacios
FOR lnObjecto = 1 TO THISFORM.OBJECTS.COUNT
 
  **--Si el objeto esta marcado con * en comment
  IF THISFORM.OBJECTS(lnObjecto).COMMENT = "*"
 
       **Obtenemos el nombre
    lcNombre = THISFORM.OBJECTS(lnObjecto).NAME
    **Obtenemos el tooltiptext
    lctooltiptext =  THISFORM.OBJECTS(lnObjecto).TOOLTIPTEXT
    **Obtenemos el valor para saber si esta vacio o no
    lcContenido = "ThisForm." + lcNombre + ".Value"
       
        **--Si el valor del objeto es vacio
        IF EMPTY(&lcContenido)
       
              **--
              MESSAGEBOX("Es necesario ingresar el dato " + "-" + lctooltiptext + "-", 16, "Error - Datos pendientes por ingresar")
           
             **--Regresamso el foco al objeto
             lcFoco = "ThisForm." + lcNombre + ".SetFocus"
              &lcFoco
              
        **--||
        RETURN
        **--||
           

        ENDIF
  ENDIF
ENDFOR
**--//


Luego, solo vamos a cada control que deseamos validar para que no tenga datos en blanco y escribimos un * en la propiedad comment. En la propiedad tooltiptext escribimos algun nombre que deseamos ver del dato para el mensaje que aparecerá si se dispara la validacion.

Prueben y me avisan.


Cualquier duda o comentario para mejorar es siempre bienvenido.

Pueden encontrarme en todoexpertos.com en la subcategoria de visualfoxpro, como el experto ravenn


Un abrazo!
Desde Managua, Nicaragua!
Ravenn :D :D :D

Cómo saber si un numero es entero o si tiene parte decimal

Hola a todos. A veces necesitamos conocer si un numero (por lo general, contenido en una variable) es entero o decimal. Aqui una solucion practica:


LOCAL lnNumero, lnExp

**--El numero en cuestion es 15.78 = DECIMAL
lnNumero = 15.78


**--Validamos para saber si es decimal o no
lnExp = lnNumero - INT(lnNumero)

**--Despues de restar la parte entera al numero,
**--quedará, de ser decimal, la parte despues del . decimal.
IF lnExp > 0

         **--Es decimal, porque el restante es mayor a 0
         **--Aqui procedemos a como deseemos

ENDIF
**--//


La cosa es tomar la expresion numerica, restar la parte entera y quedará el remanente decimal. Si ese remanente ES MAYOR a cero significa que si hay una parte decimal. Si es igual a cero, la expresion no tenia decimales... por tanto es completamente entero. Facil no?


Es solo una práctica solucion, no es la unica ni la mejor. Cualquier duda o comentario para mejorar es siempre bienvenido.

Un abrazo!
Desde Managua, Nicaragua!
Ravenn :D :D :D



Cómo obtener un numero al azar entre 1 y 1000

Hola a todos. Es un placer poder saludarlos. Hoy veremos como obtener un numero al azar entre 1 y 1000. 

Para tal fin, haremos uso de RAND(), el cual devuelve un numero aleatorio entre 0 y 1. La cosa va asi: 


LOCAL lnNumRand, lnNumeroAzar

**--Numero aleatorio
lnNumRand = RAND() * 1000

**--Tomamos la parte entera
lnNumeroAzar = INT(lnNumRand)

**--Mostramos resultado
MESSAGEBOX(lnNumeroAzar)
**--//



Con esto, tomamos solo la parte entera del numero que RAND() devuelve despues de multiplicarlo por 1000. Aqui, podemos decidir el rango hasta donde queremos calcular el numero. Si lo multiplicas por 10, sera entre 0 y 1. Si lo multiplicas por 100 el rango sera de 0 a 100... ya asi sucesivamente.


Bien... esta es solo una pequeña solucion. En el foro de todoexpertos.com han realizado algunas veces esta consulta y he aqui una forma de proceder. No es la única ni la mejor... pero esta bien.


Cualquier duda o comentario para mejorar, es siempre bienvenido.

Un abrazo!
Desde Managua, Nicaragua!
Ravenn :D :D :D