Leguaje Visual


Programación estructurada


La programación estructurada es un paradigma de programación orientado a mejorar la claridad, calidad y tiempo de desarrollo de un programa de computadora, utilizando únicamente subrutinas y tres estructuras: secuencia, selección (if y switch) e iteración (bucles for y while), considerando innecesario y contraproducente el uso de la instrucción de transferencia incondicional (GOTO), que podría conducir a "código espagueti", que es mucho más difícil de seguir y de mantener, y era la causa de muchos errores de programación.

Surgió en la década de 1960, particularmente del trabajo Böhm y Jacopini,1 y una famosa carta, la sentencia goto considerada perjudicial, de Edsger Dijkstra en 19682 — y fue reforzado teóricamente por el teorema del programa estructurado, y prácticamente por la aparición de lenguajes como ALGOL con adecuadas y ricas estructuras de control.

Orígenes de la programación estructurada

A finales de los años 1970 surgió una nueva forma de programar que no solamente daba lugar a programas fiables y eficientes, sino que además estaban escritos de manera que facilitaba su mejor comprensión, no sólo proveyendo ventajas durante la fase de desarrollo, sino también posibilitando una más sencilla modificación posterior.

El teorema del programa estructurado, propuesto por Böhm-Jacopini, demuestra que todo programa puede escribirse utilizando únicamente las tres instrucciones de control siguientes:
  • Secuencia
  • Instrucción condicional.
  • Iteración (bucle de instrucciones) con condición al principio.
Solamente con estas tres estructuras se pueden escribir todos los programas y aplicaciones posibles. Si bien los lenguajes de programación tienen un mayor repertorio de estructuras de control, éstas pueden ser construidas mediante las tres básicas citadas.



Programación Orientada a Objetos

La programación Orientada a objetos (POO) es una forma especial de programar, más cercana a como expresaríamos las cosas en la vida real que otros tipos de programación.
Con la POO tenemos que aprender a pensar las cosas de una manera distinta, para escribir nuestros programas en términos de objetos, propiedades, métodos y otras cosas que veremos rápidamente para aclarar conceptos y dar una pequeña base que permita soltarnos un poco con este tipo de programación.

Motivación
Durante años, los programadores se han dedicado a construir aplicaciones muy parecidas que resolvían una y otra vez los mismos problemas. Para conseguir que los esfuerzos de los programadores puedan ser utilizados por otras personas se creó la POO. Que es una serie de normas de realizar las cosas de manera que otras personas puedan utilizarlas y adelantar su trabajo, de manera que consigamos que el código se pueda reutilizar.
La POO no es difícil, pero es una manera especial de pensar, a veces subjetiva de quien la programa, de manera que la forma de hacer las cosas puede ser diferente según el programador. Aunque podamos hacer los programas de formas distintas, no todas ellas son correctas, lo difícil no es programar orientado a objetos sino programar bien. Programar bien es importante porque así nos podemos aprovechar de todas las ventajas de la POO.

Cómo se piensa en objetos
Pensar en términos de objetos es muy parecido a cómo lo haríamos en la vida real. Por ejemplo vamos a pensar en un coche para tratar de modelizarlo en un esquema de POO. Diríamos que el coche es el elemento principal que tiene una serie de características, como podrían ser el color, el modelo o la marca. Además tiene una serie de funcionalidades asociadas, como pueden ser ponerse en marcha, parar o aparcar.

CREATE PROJECT (Comando)
Abre el Administrador de proyectos de forma que pueda crear un proyecto. Especifica el nombre de archivo de la tabla de proyecto. Si no especifica ninguna extensión para este archivo, Visual FoxPro le asignará automáticamente la extensión .pjx.
CLOSE
Cierra varios tipos de archivo. Cierra todas las bases de datos, tablas e índices abiertos en la sesión de datos actual y en todas las sesiones de datos inactivas y selecciona el área de trabajo 1. CLOSE ALL también cierra los archivos abiertos con las funciones de archivo de bajo nivel FCREATE( ) y FOPEN( ). CLOSE ALL no cierra un archivo abierto con SET PRINT.
CLOSE ALL
Cierra todas las bases de datos, tablas e índices abiertos en la sesión de datos actual y en todas las sesiones de datos inactivas y selecciona el área de trabajo 1. CLOSE ALL también cierra los archivos abiertos con las funciones de archivo de bajo nivel FCREATE( ) y FOPEN( ). CLOSE ALL no cierra un archivo abierto con SET PRINT.
CLOSE ALL también cierra lo siguiente:
  • Diseñador de formularios
  • Administrador de proyectos
  • Diseñador de etiquetas
  • Diseñador de informes
  • Diseñador de consultas
CLOSE ALL no cierra lo siguiente:
  • La ventana Comandos
  • La ventana Depuración
  • Ayuda
  • La ventana Seguimiento
OPEN DATABASE
Abre una base de datos. Especifica el nombre de la base de datos que se va a abrir. Si no especifica una extensión para el nombre de archivo, Visual FoxPro asignará automáticamente la extensión .DBC. Si omite FileName, aparecerá el cuadro de diálogo Abrir. Puede especificar un nombre de ruta de acceso como parte del nombre de la base de datos.

SET DEFAULT especifica la unidad y el directorio por defecto de la aplicación

Clear:
 Limpia pantalla

ejemplo:
CLEAR [ALL | CLASS ClassName | CLASSLIB ClassLibraryName | DEBUG | DLLS
   [cAliasNameList]| EVENTS | FIELDS | GETS | MACROS | MEMORY 
   | MENUS | POPUPS | PROGRAM | PROMPT | READ [ALL] | RESOURCES 
   [FileName] | TYPEAHEAD | WINDOWS]

Clear Windows:

 Libera de la memoria todas las definiciones de ventanas definidas por el usuario y borra las ventanas de la ventana principal de Visual FoxPro o la ventana activa definida por el usuario. Las definiciones de ventanas se pueden guardar con SAVE WINDOW en un archivo o campo memo para su uso posterior. 
La ejecución de CLEAR WINDOWS libera cualquier referencia de variable de sistema a formularios. Por ejemplo, los comandos siguientes crean una referencia de variable de sistema para un formulario y, después, muestran información acerca de dicha variable:

Ejemplo

CLEAR WINDOWS
DISPLAY MEMORY LIKE goMyForm  && Displays GOMYFORM  O  .NULL.

Dir: Muestra el contenido de un directorio del disco.

Ejemplo

El ejemplo siguiente utiliza MKDIR para crear un nuevo directorio llamado mytstdir y, luego, usa CHDIR para cambiar al nuevo directorio. Se utiliza GETDIR( ) para mostrar la estructura de directorios y RMDIR para borrar el directorio que se acaba de crear. Se utiliza GETDIR( ) para volver a mostrar la estructura de directorios.
SET DEFAULT TO HOME( )  && Restore Visual FoxPro directory
MKDIR mytstdir  && Create a new directory
CHDIR mytstdir  && Change to the new directory
= GETDIR( )  && Display the Select Directory dialog box
SET DEFAULT TO HOME( )  && Restore Visual FoxPro directory
RMDIR mytstdir  && Remove the new directory
= GETDIR( )  && Display the Select Directory dialog box

Dir *.*: muestra todo el contenido de un directorio en el disco

Ejemplo

El ejemplo siguiente utiliza MKDIR para crear un nuevo directorio llamado mytstdir y, luego, usa CHDIR para cambiar al nuevo directorio. Se utiliza GETDIR( ) para mostrar la estructura de directorios y RMDIR para borrar el directorio que se acaba de crear. Se utiliza GETDIR( ) para volver a mostrar la estructura de directorios.
SET DEFAULT TO HOME( )  && Restore Visual FoxPro directory
MKDIR mytstdir  && Create a new directory
CHDIR mytstdir  && Change to the new directory
= GETDIR( )  && Display the Select Directory dialog box
SET DEFAULT TO HOME( )  && Restore Visual FoxPro directory
RMDIR mytstdir  && Remove the new directory
= GETDIR( )  && Display the Select Directory dialog box

Dir *.prg: muestra solo los archivos con extensión prg en el disco

Ejemplo

El ejemplo siguiente utiliza MKDIR para crear un nuevo directorio llamado mytstdir y, luego, usa CHDIR para cambiar al nuevo directorio. Se utiliza GETDIR( ) para mostrar la estructura de directorios y RMDIR para borrar el directorio que se acaba de crear. Se utiliza GETDIR( ) para volver a mostrar la estructura de directorios.
SET DEFAULT TO HOME( )  && Restore Visual FoxPro directory
MKDIR mytstdir  && Create a new directory
CHDIR mytstdir  && Change to the new directory
= GETDIR( )  && Display the Select Directory dialog box
SET DEFAULT TO HOME( )  && Restore Visual FoxPro directory
RMDIR mytstdir  && Remove the new directory
= GETDIR( )  && Display the Select Directory dialog box

Set talk off/on: Determina si Visual FoxPro muestra o no los resultados de los comandos.



ON
(Predeterminado) Permite enviar la conversación a la ventana principal de Visual FoxPro, a la ventana de mensajes del sistema, a la barra de estado gráfica o a una ventana definida por el usuario. Si SET TALK está en OFF y se cambia a ON, la conversación se dirigirá a la misma ubicación a la que se enviaba antes de ejecutar SET TALK OFF.
OFF
Impide que la conversación se envíe a la ventana principal de Visual FoxPro, a la ventana de mensajes del sistema, a la barra de estado gráfica o a una ventana definida por el usuario. Observe que para los servidores de automatización .dll en proceso el valor predeterminado de SET TALK es OFF.

Set Date to <formato>: Especifica el formato para mostrar las expresiones Date y DateTime.
ejemplo

SET DATE [TO] AMERICAN | ANSI | BRITISH | FRENCH | GERMAN | ITALIAN
 | JAPAN | TAIWAN | USA | MDY | DMY | YMD| SHORT | LONG
Nota   Cuando se establece SET DATE en SHORT o LONG, las fechas anteriores a {^1601-01-01} no serán válidas y generarán un error.
Quit: sirve para salirTermina la sesión actual de Visual FoxPro y devuelve el control al sistema operativo.

Observaciones

Para finalizar una sesión de Visual FoxPro, debe utilizar siempre el comando QUIT. Si apaga el equipo sin ejecutar QUIT, se pueden dañar los archivos abiertos y perder datos. Además, puede dejar en el disco archivos temporales de trabajo que normalmente se eliminarían.
Cancel: Finaliza la ejecución del archivo de programa actual de Visual FoxPro.

Ejemplo

En el siguiente ejemplo se simula un bucle de ejecución de programa. En cada bucle se le preguntará si desea continuar. Si presiona el botón Cancelar, CANCEL detendrá la ejecución del programa.
DO WHILE .T.
   IF MESSAGEBOX("Do you want to continue?",36) <> 6
      CANCEL
   ENDIF
ENDDO

Open data base: Abre una base de datos.

Ejemplo

En el ejemplo siguiente se usa OPEN DATABASE para abrir la base de datos testdata. DISPLAY DATABASE se usa para mostrar información sobre las tablas de la base de datos.
CLOSE DATABASES
SET PATH TO (HOME(2) + 'Data\')     && Sets path to database
OPEN DATABASE testdata  && Open testdata database
DISPLAY DATABASE  && Displays table information

Use: Abre una tabla y sus archivos de índice asociados, o una vista SQL.

Ejemplo

En el ejemplo siguiente se abren tres tablas en tres áreas de trabajo distintas. Se abre la ventana Sesión de datos para mostrar dónde están abiertas las tablas y para mostrar el alias para cada tabla.
CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'Data\testdata')
ACTIVATE WINDOW View

USE customer IN 0  && Opens Customer table
USE employee IN 0  && Opens Employee table
USE products IN 0  && Opens Products table

Append: Agrega uno o más registros nuevos al final de una tabla.

Ejemplo

El siguiente ejemplo utiliza APPEND BLANK para crear una tabla con 10 registros que contienen valores aleatorios y, a continuación, muestra los valores máximo y mínimo de la tabla.
CLOSE DATABASES
CREATE TABLE Random (cValue N(3))
FOR nItem = 1 TO 10  && Append 10 records
   APPEND BLANK
   REPLACE cValue WITH 1 + 100 * RAND( )  && Insert random values
ENDFOR

CLEAR
LIST  && Display the values
gnMaximum = 1  && Initialize minimum value
gnMinimum = 100  && Initialize maximum value
SCAN 
   gnMinimum = MIN(gnMinimum, cValue)
   gnMaximum = MAX(gnMaximum, cValue)
ENDSCAN
? 'The minimum value is: ', gnMinimum  && Display minimum value
? 'The maximum value is: ', gnMaximum  && Display maximum value
Append Blank:Agrega un registro en blanco al final de la tabla activa. Visual FoxPro no abre ninguna ventana de edición cuando usted ejecuta APPEND BLANK.
Es posible modificar los nuevos registros con BROWSE, CHANGE o EDIT.

Ejemplo

El siguiente ejemplo utiliza APPEND BLANK para crear una tabla con 10 registros que contienen valores aleatorios y, a continuación, muestra los valores máximo y mínimo de la tabla.
CLOSE DATABASES
CREATE TABLE Random (cValue N(3))
FOR nItem = 1 TO 10  && Append 10 records
   APPEND BLANK
   REPLACE cValue WITH 1 + 100 * RAND( )  && Insert random values
ENDFOR

CLEAR
LIST  && Display the values
gnMaximum = 1  && Initialize minimum value
gnMinimum = 100  && Initialize maximum value
SCAN 
   gnMinimum = MIN(gnMinimum, cValue)
   gnMaximum = MAX(gnMaximum, cValue)
ENDSCAN
? 'The minimum value is: ', gnMinimum  && Display minimum value
? 'The maximum value is: ', gnMaximum  && Display maximum value
Delete: Marca los registros que se van a eliminar.

Ejemplo

En el ejemplo siguiente se copia la estructura de CUSTOMER.DBF y todos los registros en cuyo campo country aparezca EE.UU. se copian a una nueva tabla denominada backup. Los datos de backup se copian a un archivo de texto, temp, que se abre y, luego, se elimina una vez cerrado.
CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'data\testdata')
USE customer  && Opens customer table

COPY STRUCTURE TO backup
USE backup
APPEND FROM customer FOR country = 'USA'
COPY TO temp TYPE DELIMITED

WAIT WINDOW 'Press Esc to close and erase temp.txt' NOWAIT
MODIFY FILE temp.txt NOEDIT
ERASE temp.txt
? IIF(FILE('temp.txt'),'File not deleted','File deleted')
USE
ERASE backup.dbf

Delete All: marca todos los registros y los borra

Ejemplo

En el ejemplo siguiente se copia la estructura de CUSTOMER.DBF y todos los registros en cuyo campo country aparezca EE.UU. se copian a una nueva tabla denominada backup. Los datos de backup se copian a un archivo de texto, temp, que se abre y, luego, se elimina una vez cerrado.
CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'data\testdata')
USE customer  && Opens customer table

COPY STRUCTURE TO backup
USE backup
APPEND FROM customer FOR country = 'USA'
COPY TO temp TYPE DELIMITED

WAIT WINDOW 'Press Esc to close and erase temp.txt' NOWAIT
MODIFY FILE temp.txt NOEDIT
ERASE temp.txt
? IIF(FILE('temp.txt'),'File not deleted','File deleted')
USE
ERASE backup.dbf

Delete For:

Delete next 3: borra los 3 siguientes registros.

Recall: Quita la marca de los registros marcados para eliminación en la tabla seleccionada.

Recall all: quita la marca de todos los registros marcados.  

Ejemplo
El ejemplo siguiente abre la tabla customer de la base de datos testdata. DELETE – SQL se utiliza para marcar todos los registros donde el campocountry contenga USA para su eliminación. Se muestran todos los registros marcados para ser eliminados. Se usa RECALL ALL para quitar las marcas de todos los registros marcados para eliminar.
CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'data\testdata')
USE customer  && Opens Customer table

DELETE FROM customer WHERE country = 'USA'  && Mark for deletion
CLEAR
LIST FIELDS company, country FOR DELETED( ) && List marked records
RECALL ALL  && Unmark all records marked for deletion

Recall for: 

FOR lExpression1 
Especifica que sólo se recuperarán los registros para los que el valor de lExpression1 sea verdadero (.T.). Esta opción permite filtrar los registros no deseados.

Si lExpression1 es una expresión optimizable, Rushmore optimizará RECALL FOR. Para obtener el máximo rendimiento, utilice una expresión optimizable en la cláusula FOR.
Para obtener más información, vea SET OPTIMIZE y Utilizar Rushmore para agilizar el acceso a datos.

Ejemplo
El ejemplo siguiente abre la tabla customer de la base de datos testdata. DELETE – SQL se utiliza para marcar todos los registros donde el campocountry contenga USA para su eliminación. Se muestran todos los registros marcados para ser eliminados. Se usa RECALL ALL para quitar las marcas de todos los registros marcados para eliminar.
DISPLAY STATUS

* Example 3
CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'Data\testdata')
USE Customer     && Open customer table
INDEX ON address TAG address
INDEX ON company TAG company OF custcdx
CLEAR
DISPLAY STATUS

Index on: Crea un archivo de índice para mostrar registros de tabla y tener acceso a los mismos en un orden lógico.Especifica una expresión de índice que puede incluir el nombre de campos de la tabla actual. Se crea en el archivo de índice una clave de índice basada en la expresión de índice para cada registro de la tabla. Visual FoxPro usa estas claves para mostrar registros de la tabla y tener acceso a los mismos.



Ejemplo
El ejemplo 1 abre la tabla customer y crea un archivo de índice denominado complist, que muestra y procesa los registros en el orden alfabético del campo company.
En el ejemplo 2, se abre de nuevo la tabla customer y se crea un archivo de índice llamado citycomp a partir de una subcadena de los primeros cinco caracteres del campo city y los seis primeros del campo company. Cuando se utiliza este índice, los registros de la tabla se ordenan principalmente por el campo city y como segundo criterio por el campo company.
En el ejemplo 3, se crean las etiquetas de índice. La primera etiqueta es una etiqueta de índice compuesto estructural para address. La segunda etiqueta se crea en un archivo de índice no estructural llamado custcdx.
* Example 1
CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'Data\testdata')
USE Customer     && Open customer table
INDEX ON company TO complist
CLEAR
DISPLAY STATUS
* Example 2
CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'Data\testdata')
USE Customer     && Open customer table
INDEX ON SUBSTR(city,1,5) + SUBSTR(company,1,6) TO citycomp
CLEAR
DISPLAY STATUS

* Example 3
CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'Data\testdata')
USE Customer     && Open customer table
INDEX ON address TAG address
INDEX ON company TAG company OF custcdx
CLEAR
DISPLAY STATUS


Replace: Actualiza los registros de una tabla. Especifica que los datos de FieldName1 se sustituyan por el valor de la expresión eExpression1; que los datos de FieldName2 se sustituyan por el valor de la expresión eExpression2; y así sucesivamente.
Cuando el valor de la expresión sea más largo que el ancho de un campo numérico, REPLACE hará que quepa el valor, mediante la ejecución de los pasos siguientes:

Ejemplo
El ejemplo siguiente crea una tabla con 10 registros. Se usa REPLACE para colocar valores aleatorios en un campo. MIN( ) y MAX( ) muestran los valores máximo y mínimo de la tabla.
CLOSE DATABASES
CREATE TABLE Random (cValue N(3))
FOR nItem = 1 TO 10  && Append 10 records,
   APPEND BLANK
   REPLACE cValue WITH 1 + 100 * RAND( )  && Insert random values
ENDFOR

CLEAR
LIST  && Display the values
gnMaximum = 1  && Initialize minimum value
gnMinimum = 100  && Initialize maximum value
SCAN
   gnMinimum = MIN(gnMinimum, cValue)
   gnMaximum = MAX(gnMaximum, cValue)
ENDSCAN
? 'The minimum value is: ', gnMinimum  && Display minimum value
? 'The maximum value is: ', gnMaximum  && Display maximum value
Browse: Abre la ventana Examinar y muestra los registros de la tabla en uso o seleccionada.. Especifica los campos que aparecerán en la ventana Examinar. Los campos se muestran en el orden especificado en FieldList. En la lista de campos puede incluir campos de otras tablas relacionadas. Cuando incluya un campo de una tabla relacionada, incluya delante del nombre de campo su alias de tabla y un punto.


 Ejemplo
CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'data\testdata')
USE products  && Open products table
IF _WINDOWS OR _MAC
   SET STATUS BAR ON
ENDIF
USE products
BROWSE FIELDS in_stock :V = in_stock < 100 ;
   :F ;
   :E = 'The stock amount must be less than 100'


Recall next 3: quita la marca de los siguientes 3 registros marcados.

Ejemplo

El ejemplo siguiente abre la tabla customer de la base de datos testdata. DELETE – SQL se utiliza para marcar todos los registros donde el campocountry contenga USA para su eliminación. Se muestran todos los registros marcados para ser eliminados. Se usa RECALL ALL para quitar las marcas de todos los registros marcados para eliminar.
CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'data\testdata')
USE customer  && Opens Customer table

DELETE FROM customer WHERE country = 'USA'  && Mark for deletion
CLEAR
LIST FIELDS company, country FOR DELETED( ) && List marked records
RECALL ALL  && Unmark all records marked for deletion

Pack: Borra definitivamente todos los registros marcados para eliminar de la tabla actual y reduce el tamaño de un archivo memo asociado a la tabla. Elimina el espacio no utilizado del archivo memo, pero no elimina los registros marcados para eliminar de la tabla. La información de los campos memo se almacena en un archivo memo asociado. Un archivo memo tiene el mismo nombre que la tabla y la extensión .fpt.

Ejemplo

PACK [MEMO] [DBF] [Tablename ] [IN nWorkarea | cTableAlias]

Zap: Elimina todos los registros de la tabla actual y deja sólo la estructura de la tabla.

ejemplo
ZAP   [IN nWorkArea | cTableAlias]

Go: Coloca el puntero de registro en el número de registro especificado de una tabla.

Ejemplo

CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'data\testdata')
USE products  && Opens Products table
USE customer IN 0  && Opens Customer table
GO BOTTOM IN products
CLEAR
? RECNO('products')
GO TOP
? RECNO( )     && Displays 1
GO 5
? RECNO( )     && Displays 5

Go3: coloca el puntero de registro 3 posiciones hacia delante

Ejemplo

CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'data\testdata')
USE products  && Opens Products table
USE customer IN 0  && Opens Customer table
GO BOTTOM IN products
CLEAR
? RECNO('products')
GO TOP
? RECNO( )     && Displays 1
GO 5
? RECNO( )     && Displays 5

Go top: nos permite trasladar al primer registro de la tabla.

Ejemplo

CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'data\testdata')
USE products  && Opens Products table
USE customer IN 0  && Opens Customer table
GO BOTTOM IN products
CLEAR
? RECNO('products')
GO TOP
? RECNO( )     && Displays 1
GO 5
? RECNO( )     && Displays 5

Go bottom: nos permite trasladar al último registro de la tabla.

Ejemplo

CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'data\testdata')
USE products  && Opens Products table
USE customer IN 0  && Opens Customer table
GO BOTTOM IN products
CLEAR
? RECNO('products')
GO TOP
? RECNO( )     && Displays 1
GO 5
? RECNO( )     && Displays 5

Skip: Mueve el puntero de registro hacia adelante o hacia atrás en una tabla.

Ejemplo

CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'data\testdata')
USE customer  && Opens Customer table
CLEAR

SKIP 4 IN 'customer'
? RECNO('customer')  && Displays 5
GO BOTTOM
SKIP -5
? RECNO( )

Skip -1: mueve el puntero de registro hacia atrás 1 posición.

Ejemplo

CLOSE DATABASES
OPEN DATABASE (HOME(2) + 'data\testdata')
USE customer  && Opens Customer table
CLEAR

SKIP 4 IN 'customer'
? RECNO('customer')  && Displays 5
GO BOTTOM
SKIP -5
? RECNO( )


No hay comentarios:

Publicar un comentario