Codigo Util

De ClarionWiki

En esta sección se incluyen rutinas de código que nos resultaron útiles en más de una ocasión.

Tabla de contenidos

Permitir solo una instancia del EXE en ejecucion

En Inside the global map:

INCLUDE('CWUTIL.INC'),ONCE

En Global Program Setup:

IF NOT BeginUnique('MiAPP.exe')
   BEEP(BEEP:SystemExclamation)
   YIELD()
   CASE MESSAGE('El programa ya esta ejecutando..','Ho, ho...',ICON:Asterisk,BUTTON:OK,BUTTON:OK,0)
   OF BUTTON:OK
      HALT()
   END
END

Fernando Cerini (Evolution)

Cambiar los atributos de un archivo

(Del FAQ de SoftVelocity) 1. En Global Embeds

A. Before Global Includes:

LPCSTR EQUATE(CSTRING)
DWORD EQUATE(ULONG)

B. Global Data: Equates de los artributos

FILE_ATTRIBUTE_READONLY EQUATE(00000001h)
FILE_ATTRIBUTE_HIDDEN EQUATE(00000002h)
FILE_ATTRIBUTE_SYSTEM EQUATE(00000004h)
FILE_ATTRIBUTE_ARCHIVE EQUATE(00000020h)
FILE_ATTRIBUTE_NORMAL EQUATE(00000080h)
FILE_ATTRIBUTE_TEMPORARY EQUATE(00000100h)

C. Inside Global Map:

Module('Win32.lib')
SetFileAttributes(*CSTRING, ULONG), BOOL, RAW, PASCAL, NAME('SetFileAttributesA')
GetFileAttributes(*CSTRING), ULONG, RAW, PASCAL, NAME('GetFileAttributesA')
END

2. En el codigo:

filename CSTRING(50)
filename = 'test.txt'
y# = GetFileAttributes(filename) ! Leer atributos
y# = SetFileAttributes(filename, FILE_ATTRIBUTE_READONLY)

Fernando Cerini (Evolution)


Crear directorios

Hay 2 opciones

1- Con Clarion (no se si estaran estas funciones en versiones anteriores de Clarion)

En "Inside the Global map"

Include('clib.clw')

En Local Data

MiDir Cstring(256)
Ret     Long

En tu embed

MiDir = 'Dirtest'
Ret = MkDir(MiDir)

2- Con API En Global Embeds - inside global map:

MODULE()
 DirAccess(*CSTRING,SHORT=0),SHORT,RAW,NAME('_access'),PROC
 MkDir(*CSTRING),SHORT,RAW,NAME('_mkdir'),PROC
 DirRename(*CSTRING, *CSTRING), SHORT, RAW, NAME('_rename'),PROC
 RmDir(*CSTRING),SHORT,RAW,NAME('_rmdir'),PROC
END

Y en tu embebido podes poner lo siguiente:

Var='O:\test'      ! Ojo tiene que ser Cstring
IF DirAccess(Var)<>0        !Si no existe
         MkDir(Var)                    !Crearlo
END

Tambien se puede usar DirRename y RmDir para renombrar o borrar

Fernando Cerini (Evolution)

Modificar la posicion de un informe

Este código permite modificar la posición inicial vertical y horizontal de un informe. Es útil cuando trabajamos con un formulario preimpreso o una hoja membretada y necesitamos desplazar la impresión sin necesidad de reconfigurar el programa.

El punto embebido donde se ubica es After Open the Report.

!=====================================================
! MODIFICAR LA POSICION DEL INFORME
! El pie de página modificarlo sólo si se usa: 
! Por ejemplo se pone ahí el Nro de página o algo así.
! No es necesario cambiar los atributos de las bandas 
! de detalle adicionales que se usan en el informe.
!-----------------------------------------------------
DesplazamientoX =  getini('Parametros',|
                   'DesplazamientoX',,'.\prog.ini')
DesplazamientoY = getini('Parametros',|
                   'DesplazamientoY',,'.\prog.ini')

if DesplazamientoX or DesplazamientoY then
   SETTARGET(Report)
   x# = report{prop:Xpos}
   y# = report{prop:Ypos}
   x# += DesplazamientoX
   y# += DesplazamientoY
   target{prop:Xpos} = x#
   target{prop:Ypos} = y#
   settarget

   SETTARGET(Report,?Encabezado)
   x# = ?Encabezado{prop:Xpos}
   y# = ?Encabezado{prop:Ypos}
   x# += DesplazamientoX
   y# += DesplazamientoY
   ?Encabezado{prop:Xpos} = x#
   ?Encabezado{prop:Ypos} = y#
   settarget

   SETTARGET(Report,?PiePagina)
   x# = ?PiePagina{prop:Xpos}
   y# = ?PiePagina{prop:Ypos}
   x# += DesplazamientoX
   y# += DesplazamientoY
   ?PiePagina{prop:Xpos} = x#
   ?PiePagina{prop:Ypos} = y#
   SETTARGET
end



Generar un informe a partir de una cola en memoria

(Sin pasar por el template)

Se puede definir en el módulo en la parte de DATA el reporte necesario y mandarlo a imprimir.

En DATA SECTION

WMFQue        QUEUE
PageImage       STRING(64)
              END
ReportRunDate LONG
ReportRunTime LONG
!!> Report (portrait) 
Report  REPORT,AT(1000,2000,6000,7000),THOUS,PRE(RPT),FONT('Arial',10)
        HEADER,AT(1000,1000,6000,1000)
        END
Detail   DETAIL
        END
        FOOTER,AT(1000,9000,6000,1000)
        END
        FORM,AT(1000,1000,6000,9000)
        END
      END


Luego, en el botón que imprime, poner el siguiente código:

ReportRunDate = today()
ReportRunDate = today()
ReportRunTime = clock()
OPEN(Report)
LOOP x# = 1 to records(ColaError)
   get(ColaError,x#)
   PRINT(rpt:DetalleUno)
END
ENDPAGE(Report)
ReportPreview(WMFQue)
IF GlobalResponse = RequestCompleted
   Report{PROP:FlushPreview} = True
END
CLOSE(Report)
FREE(WMFQue)

Este proceso usa el Print Preview de Clarion para visualizar el informe, por consiguiente, la apariencia es totalmente normal para el usuario

Proceso BATCH que funcione sincronizado con el TIMER de la ventana.


En un proceso BATCH hecho a mano se puede operar en forma similar al report o al proccess, para eso hay que definir una pantalla que tenga el atributo TIMER (un valor de 1 es suficiente). El código fuente quedaría de la siguiente manera:

open(PantaTrabaja)
Accept
case event()
  of event:openwindow
     Display()
     ! Abrir pantalla, Abrir archivos, hacer el set 
     ! Inicial
  of event:timer
     ?Mensaje{prop:text} = 'Procesando...'
     display(?Mensaje)
     loop 2 times
        next(archivo)
        if errorcode() then 
           FinArchivo = true
           Break
        end
        ! HACER AQUÍ ALGÚN PROCESO..
     end
  of event:closewindow
     Setcursor()
     Close(PantaTrabaja)
     Break
  End!case

case field()
  of ?BotonCancelar
     if event() = event:accepted then
        message('Proceso cancelado por el usuario.')
        post(event:closewindow)
     end
  End
end!accept


Drop Combo a mano, con opción a todos y recuerdo de último elegido

En muchos de mis informes, cuando pido parámetros asumo lo siguiente, por ejemplo: Si el código de cliente es igual a 0 (cero) se imprimen todos los clientes, sino, se imprime uno solo. Este código permite armar lo mismo con un DropCombo, agregando la opción TODOS LOS XXXX (código Cero).

1. Definir una cola con la siguiente estructura ( por ejemplo ):

Cola     QUEUE,PRE(col)
descri     STRING(20)
codigo     SHORT
         END

2. Definir una variable local llamada por ejemplo

Combo1     STRING(20)

3. En la pantalla definir una drop combo a mano, en el campo FROM poner el nombre de la COLA, y en el USE poner Combo1 ( no poner ?Combo1 )

4. Cargar la cola según corresponda después de abrir archivos, poniendo como último dato el código 0 y el texto 'TODOS LOS REGISTROS' y hacer un ADD(Cola,1) para que quede al principio.

5. Definir una variable global ( o local estática) que va a guardar el resultado elegido:

Glo:codigo    short     

6. En el evento open window, ANTES de abrir la ventana va el siguiente código

loop x# = 1 to records(Cola)
  get(Cola,x#)
  if col:codigo = glo:codigo then break.
end
Combo1 = col:descri
?Combo1{prop:selected} = x#

7. Al hacer esto, el combo se abre posicionado en el último elemento elegido o en TODOS LOS REGISTROS si es la primera vez.


En un report, poner los totales en otra página

En algún informe, se puede solicitar como parámetro la opción de imprimir los totales correspondientes en una página nueva. Para ello, después de abrir el report:


if glo:totpag then
   settarget(report)
   ?TituloTotalCategoria{prop:pagebefore} = 1
end

En un report, cambiar el color de un campo según alguna condición

Si durante la impresión de un informe se desea cambiar algún atributo (tal como el color) de un campo puede hacer lo siguiente (antes de imprimir el detalle):

if cl:impotota <> cl:totacalc
   settarget(report)
   ?ARC:importe{PROP:FONTCOLOR} = COLOR:RED
   settarget(ProgressWindow)
else
   settarget(report)
   ?ARC:importe{PROP:FONTCOLOR} = COLOR:NONE
   settarget(ProgressWindow)
end
print(rpt:Detalle)  ! Imprimir la línea de detalle

Cerrar todas las ventanas abiertas

LOOP Thrd# = 2 TO 64 !1 es el Frame
    POST(Event:CloseWindow,,Thrd#)
END

Fernando Cerini (Evolution)

Abrir cualquier archivo con ShellExecute

Hay varios templates gratis que implementan Shellexecute, por ejemplo: http://www.sterlingdata.com/shellex.htm

Para hacerlo con codigo:

En Global-embed 'Inside the Global Map':

Module('Win32.lib')
ShellExecute(Long,*CString,*CString,*CString,*CString,Short),UShort,PASCAL,RAW,NAME('ShellExecuteA')
END

En Local Data

LOC:Handle LONG
LOC:Op     CSTRING (255)
LOC:File   CSTRING (255)
LOC:Path   CSTRING (255)
LOC:Param  CSTRING (255)
LOC:Show   LONG
LOC:RetHandle LONG

En el embed

LOC:Handle = 0{PROP:Handle}
LOC:Op     = 'Open'
LOC:File   = 'C:\TEST.TXT'
LOC:Path   = PATH()
LOC:Param  = ' '
LOC:Show   = 1
LOC:RetHandle =  ShellExecute(LOC:Handle,LOC:Op,LOC:File,LOC:Param,LOC:Path,LOC:Show)
If LOC:Rethandle <> 0 Then
  Message('Error','Error',Icon:Exclamation)
End

Fernando Cerini (Evolution)

Abrir una pagina WEB con ShellExecute

(Ver declaraciones del API y variables en el ejemplo anterior)

En el embed

LOC:Handle = 0{PROP:Handle}
LOC:Op     = 'Open'
LOC:File   = 'http://www.evolutionconsulting.com.ar'
LOC:Path   = ' '
LOC:Param  = ' '
LOC:Show   = 1
LOC:RetHandle = ShellExecute(LOC:Handle,LOC:Op,LOC:File,LOC:Param,LOC:Path,LOC:Show)

Fernando Cerini (Evolution)


Convertir un LONG a un String Binario

binario= !binario es CSTRING
LOOP F# = 1 TO 32
   IF BAND(abinario, 1) !abinario es LONG
       binario =  '1' & binario
   ELSE
       binario = '0' & binario
   END
   abinario = abinario / 2
   if abinario = 0 then break.
END

Fernando Cerini (Evolution)

Convertir un string binario a LONG

Para volver del CSTRING al LONG seria simplemente

X# = EVALUATE (binario & 'b')

Fernando Cerini (Evolution)

Copiar la QUEUE (solo lo que se esta viendo) de un browse a Excel

Copiar= !Cstring de 4.000.000
LOOP C#= 1 TO BRW1.View{Prop:Fields}
!Primero una fila con los titulos
    Copiar= Copiar & ?Browse:1{PropList:Header,C#} & '<9>'
END
Copiar= Copiar &'<13,10>'
LOOP F# = 1 TO RECORDS(BRW1.Q)
   LOOP C#= 1 TO BRW1.View{Prop:Fields}
       GET(BRW1.Q, F#)
       Copiar= Copiar & FORMAT(WHAT(BRW1.Q, C#), ?Browse:1{PropList:Picture,C#}) & '<9>'
   END
   Copiar= Copiar &'<13,10>'
END
SETCLIPBOARD(Copiar)

Fernando Cerini (Evolution)

Copiar el VIEW (cuidado: se lee todo) de un browse a Excel

Copiar= !CSTRNG de 4.000.000
LOOP C#= 1 TO BRW1.View{Prop:Fields}
    Copiar= Copiar & ?Browse:1{PropList:Header,C#} & '<9>'
END
Copiar= Copiar &'<13,10>'

SET (BRW1.View)
LOOP
   NEXT(BRW1.View)
   IF ERRORCODE() THEN BREAK.
   BRW1.SetQueueRecord
   LOOP C#= 1 TO BRW1.View{Prop:Fields}
       Copiar= Copiar & FORMAT(WHAT(BRW1.Q, C#), ?Browse:1{PropList:Picture,C#}) & '<9>'
   END
   Copiar= Copiar &'<13,10>'
END
SETCLIPBOARD(Copiar)

Fernando Cerini (Evolution)

Para que no lea todo el contenido del archivo, sino que procese estrictamente lo mismo que muestra el browse, basta con agregar la antes del BRW1.SetQueueRecord el llamado a BRW1.ValidateRecord().

 ...
 LOOP
   NEXT(BRW1.View)
   IF ERRORCODE() THEN BREAK.
   IF BRW1.ValidateRecord() THEN CYCLE.
   BRW1.SetQueueRecrod
 ...

Daniel Ruzo

Mostrar iconos en un listbox desde una queue

Ejemplo: marcar en una cola de memoria si un empleado tiene entrada y/o salida

Primero agregar los iconos al Project

En el Codigo:

cola_empleados    queue, pre(que)
nombre        string(30)
entro          long
entro_ico    long
salio          long
salio_ico    long
                        end

En el list:

primer campo: nombre

segundo campo: entro, picture @p p, iconized, transparente

tercer campo: salio, picture @p p, iconized, transparente

En el init de la pantalla:

?list{prop:iconlist,1} = '~no.ico'
?list{prop:iconlist,2} = '~si.ico'

Donde cargo la cola y la muestro:

if condicion  (si NO hay entrada)
    que:entro_ico = 1    ! icono de no
else
    que:entro_ico = 2    !icono de si
end
if condicion  (si NO hay salida)
    que:salio_ico = 1    ! icono de no
else
    que:salio_ico = 2    !icono de si
end
! otras asignaciones
add(cola_empleados)
!
display(?list)

Fernando Cerini (Evolution)

Calcular Dias Habiles

Ante todo necesitas una tabla de feriados, con al menos un campo llamado, por ej., diaferiado y una clave por dicho campo.

Luego podes hacer lo siguiente

habiles# = 0
loop dia# = FechaInicial to FechaFinal
 if (Dia# % 7) = 0 then cycle. ! porque es domingo
 if (Dia# % 7) = 6 then cycle. ! porque es sabado
 !
 ! busco si es feriado
 !
 clear(feriado:record)
 feriado:diaferiado = dia#
 if access:feriado.fetch(Feriado:PorDia) = level:benign then cycle. !porque es feriado
 !
 habiles# += 1
end!loop
corridos# = fechafinal - fechainicial

Si los calculos que vas a realizar son muchos y continuos, sería conveniente que la tabla de feriados la cargas en una queue y realices las búsquedas sobre ella.

Adrian Gallegos - Mega Sistemas S.R.L.

Ultimo dia del mes y cantidad de dias (Lunes, Martes, etc) entre 2 Fechas

Incluye el truco de saber el ultimo dia del mes: en la parte " Date(4,1,2005)-1 " significa que le resto 1 al primer dia del mes siguiente, lo cual es una forma de obtener el ultimo dia del mes actual...

Loop Fecha# = Date(3,1,2005) TO (Date(4,1,2005)-1)
   EXECUTE (Fecha# % 7) + 1
         Domingo# +=1
         Lunes# +=1
         Martes# +=1
         Miercoles# +=1
         Jueves# +=1
         Viernes# +=1
         Sabado# +=1
   END
END

Fernando Cerini (Evolution)


Fecha en Español (por ejemplo en el Frame)

Si quieres que funcione independientemente de como este configurado windows, lo mejor es poner este embed, al final de WindowManager.Init

EXECUTE (TODAY() % 7) + 1
Dia"= 'Domingo'
Dia"= 'Lunes'
Dia"= 'Martes'
Dia"= 'Miercoles'
Dia"= 'Jueves'
Dia"= 'Viernes'
Dia"= 'Sabado'
END
EXECUTE (MONTH(TODAY()))
Mes" = 'Enero'
Mes" = 'Febrero'
Mes" = 'Marzo'
Mes" = 'Abril'
Mes" = 'Mayo'
Mes" = 'Junio'
Mes" = 'Julio'
Mes" = 'Agosto'
Mes" = 'Septiembre'
Mes" = 'Octubre'
Mes" = 'Noviembre'
Mes" = 'Diciembre'
END
AppFrame{Prop:StatusText,1} = CLIP(Dia") & ' ' & DAY(TODAY()) & ' de ' &
CLIP(Mes")


Fernando Cerini (Evolution)

Dia" = Choose(((TODAY() % 7) + 1),'Domingo','Lunes','Martes','Miercoles','Jueves','Viernes','Sabado')

Javier A. Junca Barreto (SICyA Software - Colombia)

Obtener la fecha del server

Este codigo lee la fecha del servidor, usando el truco de crear un archivo en el servidor y leer la fecha y hora de los atributos:

!Data
LOC:TMP STRING(254),STATIC
TMP FILE,DRIVER('Ascii'),CREATE,NAME(LOC:TMP)
RECORD  RECORD
LIN STRING(1)
   .
   .
FILS   QUEUE(File:queue),PRE(FIL)
      END
CODE
 LOC:TMP = PATH()&'\TMP'&RANDOM(10000,99999)&'.TMP'
 CREATE(TMP)
 IF NOT ERRORCODE()
   DIRECTORY(FILS,LOC:TMP,0)
   REMOVE(TMP)
   GET(FILS,1)
   IF TODAY() <> FIL:DATE OR ABS(CLOCK()-FIL:TIME) > 100
!FECHA DIFERENTE O 1 SEGUNDO DE DESFASE
     SETTODAY(FIL:DATE)
     SETCLOCK(FIL:TIME)
   END
 ELSE
   REMOVE(TMP)
 END

Carlos Gutierrez

Con SQL

La sugerencia de Carlos es muy buena. Si estas usando SQL o drivers ODBC, la otra opcion es preguntarle la fecha al motor de base de datos.

La forma generica de hacerlo es:

temp{prop:sql}='SELECT {fn curdate() }'

Con NET TIME

Posteado por Diego Sánchez al foro.

Run('NET TIME \\Server_Name /SET /Y')

Reemplazar "Server_Name" por el nombre del servidor o equipo del cual se desea obtener la hora Fue posteado originalmente por un NICOLAS VEILLEUX nveilleux@nbautomation.com, en el foro comp.lang.clarion


Fernando Cerini (Evolution)

Llamar a un Stored Procedure

El codigo seria mas o menos asi

L:Query = 'CALL NombreDelStored ( & FORMAT(ParamFecha,@D12) & ,  &
FORMAT(OtraFecha,@D12) & , ' & OtroParam1 &', ' & OtroParam2 &', ' &
OtroParam3 &'  )'
ResSQL{prop:sql} = L:Query
Loop Until Access:ResSql.Next()
   MiVariable = R:Campo1
   etc     = R:Campo2
....
END

En la tabla auxiliar ResSQL obtienes el resultado del ultimo SELECT que tenga el Stored Procedure.

Para mas detalles ver el documento sobre SQL Embebido (Evolution Downloads)

Tambien recomiendo leer el help "MSSQL Accelerator Calling a Stored Procedure". Ahi esta explicado ademas el uso de valores de retorno y parametros de salida.

Fernando Cerini (Evolution)

Encriptacion basica

Encriptacion / desencriptacion basica de un campo usando el metodo XOR.

!la primera vez encripta
!al volver a aplicar el algoritmo con la misma
!Clave de encriptado: desencripta
X# = 1
loop Y# = 1 to Len(Campo)
  Campo [Y#] = chr(bxor(val(Campo[Y#]), val(ClaveEncriptado[X#])))
  X# += 1
  if X# > len (ClaveEncriptado) then X# = 1.
end
display
!Campo y ClaveEncriptado son campos CString

Fernando Cerini (Evolution)

Generar un archivo de Texto a maxima velocidad

Estas son las APIs para generar un archivo de texto sin necesidad de declararlo en el Diccionario.

Ademas es muy rapido, ideal para exportaciones.

En Global - Inside Global map:

MODULE('Windows API')
 _lcreat(*CSTRING,SIGNED),SIGNED,PASCAL,RAW
 _hwrite(SIGNED,*CSTRING,LONG),LONG,PASCAL,RAW
 _lclose(SIGNED),SIGNED,PASCAL
END


Por ejemplo para guardar el contenido de un control Text

IF NOT FILEDIALOG('Guardar como',FileName,'Text|*.TXT|Source|*.CLW',FILE:Save + FILE:LongName)
   CYCLE
END
F# = _lcreat(FileName,0)
X# = _hwrite(F#,Texto,LEN(Texto))
X# = _lclose(F#)

Fernando Cerini (Evolution)

Efecto BLINK en un campo

Tienes que crear un timer en la ventana, para eso ponle cada cuanto se va a ejecutar en la propiedad timer de la ventana. Son centesimas de seg, asi que si le pones 50 por ejemplo tu campo va a titilar 2 veces por segundo.

Luego cierra la ventana, vuelve a entrar y vas a encontrar un evento timer de la ventana en los embeds.

Window Events --> Timer

if ?campo{prop:background} = COLOR:WHITE
   ?campo{prop:background} = COLOR:SILVER
else
   ?campo{prop:background} = COLOR:WHITE
end

Fernando Cerini (Evolution)

FUNCION PARA CALCULAR DIGITO VERIFICADOR EN CUIT

- La siguiente función devuelve el numero de cuit con el dígito verificador correcto. - El parámetro que recibe es el numero de cuit a revisar incluyendo el dígito verificador.

 ...
 CuitCliente='20-15433984-6'
 IF Cuit(CuitCliente)=CuitCliente THEN
   MESSAGE('Digito verificador correcto')
 ELSE
   MESSAGE('Digito verificador incorrecto')
 END
 ...

Cuit         PROCEDURE(cuit1)
cuit2        STRING(255)
digver       LONG
lon          LONG
fac          LONG
car          STRING(1)
  
 CODE
 cuit2=cuit1
 digver=0
 fac=2
 lon=LEN(CLIP(cuit2))
 LOOP i#=lon-1 TO 1 BY -1
   car=SUB(cuit2,i#,1)
   IF car<'0' OR car>'9' THEN
     CYCLE
   .
   digver=digver+(car*fac)
   fac+=1
   IF fac>7 THEN
     fac=2
   .
 .
 digver=11-(digver%11)
 IF digver>9 THEN
   digver=0
 .
 cuit2=SUB(cuit2,1,lon-1) & FORMAT(digver,@n01)
 RETURN(cuit2)

Este codigo esta en la documentacion del template de Impresoras Fiscales (BIGSYS TEMPLATES) del amigo Juan Carlos Rodríguez

Validar Email

Puedes hacerlo con MATCH, el cual devuelve 1 o 0 si el mail no es valido. Ejemplo:

X# =  MATCH(UPPER(CLIP(locemail)),|
'^[-A-Z0-9._]+@{{[-A-Z0-9._]+.}+[A-Z][A-Z][A-Z]?[A-Z]?$', Match:Regular)

Fernando Cerini (Evolution)

Restar Horas

Para sacar la diferencia entre horas es simplemente:

resultado = hora2  - hora + 1

El +1 es porque sino que faltaria un segundo cuando muestres el resultado (en formato @T6, por ej)

Si Hora2 es del dia siguiente, la cuenta seria:

resultado = (hora2 +(100*60*60*24)) - hora + 1

Fernando Cerini (Evolution)

Digito Verificador para 5 digitos

Si tenes un numero de 5 digitos y deses verificar que el mismo es ingresado correctamente podes usar este codigo que genera un digito verificador

Crea una funcion que tenga un parametro (numero a verificar) y retorne el digito verificador correspondiente ejem: DigitoV5(LONG xNumero),BYTE

 loc:Numero = xNumero

 loc:Valor = (loc:Numero[1]*5) + |
             (loc:Numero[2]*4) + |
             (loc:Numero[3]*3) + |
             (loc:Numero[4]*2) + |
             (loc:Numero[5]*7)

 IF (loc:Valor%5) + 1 = 0 OR (loc:Valor%5) + 1 = 1
   loc:Digito = 0
 ELSE
   loc:Digito = 6 - ((loc:Valor%5) + 1)
 END

 RETURN loc:Digito

Ruben Garcia (DiPS)

Digito Verificador para Cualquier Longitud

Si no sabes que longitud puede tener el numero a verificar podes probar verificarla con este codigo

Crea una funcion cuyo parametro es el numero a verificar y retorne el digito verificador. Ej. DigitoV(STRING xNumero),BYTE

 !Inicializa
 loc:Numero   = xNumero
 loc:Valor    = 0
 loc:Multiplo = 1

 !Barrido y calculo
 LOOP loc:Posicion = LEN(CLIP(loc:Numero)) TO 1 BY -1
   loc:Multiplo += 1
   IF loc:Multiplo > 7
     loc:Multiplo = 2
   END
   loc:Valor += loc:Numero[loc:Posicion] * loc:Multiplo
 END

 loc:Digito = loc:Valor % 11

 IF loc:Digito = 10
   loc:Digito = 0
 END

 RETURN loc:Digito

Ruben Garcia (DiPS)

Autoincremento Manual

Esto se utiliza cuando tenemos una tabla en la cual queremos manejar la clave de auto incremento

CLEAR(MASTER)
SET(MAS:ClavePorID, MAS:ClavePorID)
ACCES:MASTER.Previous()
IF MAS:Id = 0 THEN 
    MAS:Id = 1
ELSE 
    MAS:Id = MAS:Id +1 
END 

!!!MAS:Id TENDRÍA EL VALOR DEL PROXIMO NUMERO.-

Gracias Fernando Cerini Evolution Consulting

Herramientas personales