Programación con C/AL para Microsoft Business Solutions Navision

55,141 views

Published on

Este pequeño curso toca todos los aspectos esenciales del lenguaje de programación C/AL dentro del entorno de Navision

Published in: Business
27 Comments
25 Likes
Statistics
Notes
No Downloads
Views
Total views
55,141
On SlideShare
0
From Embeds
0
Number of Embeds
441
Actions
Shares
0
Downloads
0
Comments
27
Likes
25
Embeds 0
No embeds

No notes for slide

Programación con C/AL para Microsoft Business Solutions Navision

  1. 1. Programación en Lenguaje C/AL para Microsoft Business Solutions Navision Pablo Espada Bueno www.esbupa.com www.programadorautonomo.net Nota: Microsoft, Navision, C/AL, etc… son marcas registradas de Microsoft
  2. 2. <ul><li>Habitualmente me dedico a impartir formación y a labores de desarrollo y consultoría en .NET </li></ul><ul><li>También imparto formaciones sobre Desarrollo en Navision </li></ul><ul><li>Si desea que colabore con usted impartiéndoles una formación o desarrollando algún proyecto, puede contactarme: </li></ul><ul><ul><li>Web </li></ul></ul><ul><ul><ul><li>www.programadorautonomo.net </li></ul></ul></ul><ul><ul><ul><li>www.esbupa.com </li></ul></ul></ul><ul><ul><li>Email </li></ul></ul><ul><ul><ul><li>[email_address] </li></ul></ul></ul><ul><ul><ul><li>[email_address] </li></ul></ul></ul><ul><li>Espero que les guste la presentación </li></ul>Contacto
  3. 3. Lenguaje C/AL - Indice <ul><li>Tipos de datos </li></ul><ul><li>Variables </li></ul><ul><li>Funciones </li></ul><ul><li>Operadores </li></ul><ul><li>Sentencias </li></ul><ul><ul><li>Asignación </li></ul></ul><ul><ul><li>Decisión (IF, CASE) </li></ul></ul><ul><ul><li>Iteración (FOR, REPEAT, WHILE) </li></ul></ul><ul><ul><li>Otras </li></ul></ul><ul><li>Comentarios </li></ul><ul><li>Tablas </li></ul><ul><li>Acceso a objetos </li></ul><ul><li>Formularios </li></ul><ul><li>Informes </li></ul><ul><li>Unidades de código </li></ul><ul><li>DataPorts </li></ul><ul><li>Funciones comunes </li></ul><ul><li>SumIndexFields </li></ul><ul><li>Flowfields </li></ul><ul><li>Diálogos </li></ul><ul><li>Acceso a ficheros </li></ul><ul><li>Depurador </li></ul><ul><li>Seguimiento de Código </li></ul><ul><li>Asistente para código </li></ul><ul><li>OCX </li></ul><ul><li>BLOB </li></ul><ul><li>Formateo estándar </li></ul>
  4. 4. Introducción <ul><li>Lenguaje 4GL para trabajar en entorno Navision </li></ul><ul><li>Especializado en trabajo sobre la BBDD relacional </li></ul><ul><li>Integrado en entorno Windows </li></ul><ul><ul><li>OCX para acceso a datos de otras aplicaciones </li></ul></ul><ul><ul><li>Acceso a ficheros </li></ul></ul>
  5. 5. Tipos de datos <ul><li>Variables fundamentales: Numéricas </li></ul><ul><ul><li>Integer </li></ul></ul><ul><ul><li>Números entre -2.147.483.647 y +2.147.483.647. </li></ul></ul><ul><ul><li>BigInteger </li></ul></ul><ul><ul><li>Número de 64 bits. </li></ul></ul><ul><ul><li>Se usa L para indicar que es un entero largo: bi := 3983200032984209L. </li></ul></ul><ul><ul><li>Decimal </li></ul></ul><ul><ul><li>Números entre -10E63 y +10E63. </li></ul></ul><ul><ul><li>18 dígitos significativos. </li></ul></ul><ul><ul><li>Char </li></ul></ul><ul><ul><li>Un carácter entre 0 y 255, convertible libremente de entero a carácter. </li></ul></ul><ul><ul><li>Operable como un integer o como un carácter: </li></ul></ul><ul><ul><li>C := 'A'; </li></ul></ul><ul><ul><li> C := S[2]; </li></ul></ul><ul><ul><li>C := C + 1; </li></ul></ul><ul><ul><li>Option </li></ul></ul><ul><ul><li>Número de opción entre -2.147.483.647 y +2.147.483.647 , convertible libremente de entero a opción. Las opciones se declaran simbólicamente con la propiedad OptionString de la variable: OptionString = Oferta,Pedido,Factura,Abono </li></ul></ul>
  6. 6. Tipos de datos (II) <ul><li>Variables fundamentales: De Cadena </li></ul><ul><ul><li>Text </li></ul></ul><ul><ul><li>Cadenas de hasta 250 caracteres. </li></ul></ul><ul><ul><li>Sus caracteres son indizables: </li></ul></ul><ul><ul><li>Nombre[3] </li></ul></ul><ul><ul><li>Code </li></ul></ul><ul><ul><li>Cadenas de texto de hasta 250 caracteres. </li></ul></ul><ul><ul><li>En mayúsculas. </li></ul></ul><ul><ul><li>El sistema hace automáticamente la conversión, y quita espacios iniciales y finales. Sus caracteres son indizables: </li></ul></ul><ul><ul><li>CodFormasPago[3] </li></ul></ul>
  7. 7. Tipos de datos (III) <ul><li>Variables fundamentales: Fecha y Hora </li></ul><ul><ul><li>Date </li></ul></ul><ul><ul><li>Almacena una fecha. </li></ul></ul><ul><ul><li>Se inicializa con 0D. </li></ul></ul><ul><ul><li>hoy := 220704D; </li></ul></ul><ul><ul><li>Time </li></ul></ul><ul><ul><li>Almacena una hora </li></ul></ul><ul><ul><li>Se inicializa con 0T </li></ul></ul><ul><ul><li>hora := 123000T; </li></ul></ul><ul><ul><li>DateTime </li></ul></ul><ul><ul><li>Almacena una hora UTC. Siempre se mostrará adaptada al horario local </li></ul></ul><ul><ul><li>Se inicializa con 0T </li></ul></ul><ul><ul><li>fechahora := December 31, 2004, 14:20:59.999.; </li></ul></ul>
  8. 8. Tipos de Datos (y IV) <ul><li>Variables fundamentales: Booleanas </li></ul><ul><ul><li>Boolean </li></ul></ul><ul><ul><li>Valores lógicos: TRUE o FALSE </li></ul></ul>
  9. 9. Tipos Complejos <ul><li>C/AL incluye un conjunto de tipos complejos utilizados en ciertos casos </li></ul><ul><li>BLOB </li></ul><ul><ul><ul><li>Se utiliza para almacenar valores binarios (imágenes, ficheros, videos, etc..) y se almacena de forma externa al registro de la base de datos. Tamaño máx 2GB </li></ul></ul></ul><ul><li>Record </li></ul><ul><ul><ul><li>Un record se asocia con un registro de cada tabla. </li></ul></ul></ul><ul><ul><ul><li>El acceso a los campos se realiza escribiendo el nombre del registro, un punto y el nombre del campo: </li></ul></ul></ul><ul><ul><ul><li>EJ: Customer.”No.” </li></ul></ul></ul>
  10. 10. Tipos Complejos (II) <ul><li>Form </li></ul><ul><ul><li>Representa un Formulario. Cada formulario estará compuesto por un conjunto de controles </li></ul></ul><ul><li>Report </li></ul><ul><ul><li>Representa un Informe </li></ul></ul><ul><li>Codeunit </li></ul><ul><ul><li>Contenedores de código, organizado en funciones </li></ul></ul><ul><li>File </li></ul><ul><ul><li>Tipo Fichero. Permite acceder a un fichero del sistema de ficheros </li></ul></ul>
  11. 11. Tipos Complejos (III) <ul><li>Dialog </li></ul><ul><ul><li>Representa un cuadro de diálogo. </li></ul></ul><ul><li>DateFormula </li></ul><ul><ul><li>Representa una fórmula para la función CALCDATE </li></ul></ul><ul><li>GUID </li></ul><ul><ul><li>Identificador Único del sistema. 16 bytes: </li></ul></ul><ul><ul><ul><li>12345678-1234-1234-1234-1234567890AB </li></ul></ul></ul><ul><li>TableFilter </li></ul><ul><ul><li>Permite aplicar un filtro a una tabla. Sólo utilizado para Permisos </li></ul></ul>
  12. 12. Tipos Complejos (IV) <ul><li>RecordRef </li></ul><ul><ul><li>Puntero a un Registro. Se diferencia con el tipo Record en que, a priori, desconocemos la tabla a la que va a apuntar. </li></ul></ul><ul><ul><li>El equivalente para campos es el FieldRef </li></ul></ul><ul><ul><li>El equivalente para claves es el KeyRef </li></ul></ul><ul><li>RecordID </li></ul><ul><ul><li>Almacena el ID de la Tabla junto con la Clave Primaria. </li></ul></ul><ul><li>InStream y OutStream </li></ul><ul><ul><li>Permiten leer y escribir BLOB’s </li></ul></ul><ul><li>Variant </li></ul><ul><ul><li>Tipo Indefinido necesario para el uso de OCX </li></ul></ul><ul><ul><li>Contiene: record, file, action, codeunit, Automation, boolean, option, integer, decimal, char, text, code, date, time, binary, DateFormula, TransactionType, InStream and OutStream. </li></ul></ul><ul><li>BigText </li></ul><ul><ul><li>Similar al BLOB pero sólo para contenidos de texto. MAX 2GB </li></ul></ul>
  13. 13. Variables <ul><li>Inicialización </li></ul><ul><li>C/AL inicializa las variables por defecto a los siguientes valores: </li></ul><ul><ul><li>Boolean: FALSE </li></ul></ul><ul><ul><li>Numeric: 0 </li></ul></ul><ul><ul><li>Strings: '' </li></ul></ul><ul><ul><li>Date: 0D </li></ul></ul><ul><ul><li>Time: 0T </li></ul></ul>
  14. 14. Variables (II) <ul><li>Definición </li></ul><ul><li>Las variables en C/AL pueden ser : </li></ul><ul><ul><li>Locales : Su ámbito es la función donde se definen </li></ul></ul><ul><ul><li>Globales : Su ámbito es el objeto donde se definen </li></ul></ul>
  15. 15. Variables (III) <ul><li>Ejemplo: </li></ul><ul><ul><li>Crearemos una nueva CodeUnit </li></ul></ul><ul><ul><li>Definiremos un conjunto de variables (locales y globales) </li></ul></ul><ul><ul><li>Podemos crear una variable Option y mostrar sus valores así: </li></ul></ul><ul><ul><ul><li>MESSAGE('The value of %1 is %2','LoopNo',LoopNo); </li></ul></ul></ul>
  16. 16. Variables (IV) <ul><li>Arrays y matrices </li></ul><ul><li>Se definen con la propiedad Dimensions de la variable </li></ul><ul><ul><li>Dimensions=4;3 </li></ul></ul><ul><li> Se las referencia con corchetes </li></ul><ul><ul><li>SaldoCtaBanco[2,2] </li></ul></ul><ul><li>los índices tienen rango 1…n </li></ul><ul><li>Funciones: </li></ul><ul><li>CLEAR (Borra todo el Array) </li></ul><ul><li>ARRAYLEN (Devuelve la Longitud del Array) </li></ul>
  17. 17. Variables del Sistema <ul><li>Variables del sistema (system-defined variables) </li></ul><ul><ul><li>C/SIDE las crea y deja disponibles para el programador en ciertos contextos </li></ul></ul><ul><ul><li>Rec </li></ul></ul><ul><ul><li>Cuando se modifica un registro, Rec contiene el registro en su estado modificado. </li></ul></ul><ul><ul><li>xRec </li></ul></ul><ul><ul><li>Cuando se modifica un registro, xRec contiene el registro antes de la modificación. </li></ul></ul><ul><ul><li>CurrForm </li></ul></ul><ul><ul><li>Variable que representa el objeto Form actual. </li></ul></ul><ul><ul><li>CurrReport </li></ul></ul><ul><ul><li>Variable que representa el objeto Report actual. </li></ul></ul><ul><ul><li>RequestOptionsForm </li></ul></ul><ul><ul><li>Variable que representa el formulario de diálogo de entrada al objeto Report actual. </li></ul></ul><ul><ul><li>CurrFieldNo </li></ul></ul><ul><ul><li>El número de campo del campo actual desde el que se llamó al disparador. </li></ul></ul>
  18. 18. Funciones <ul><li>Definición </li></ul><ul><li>La función se define con un nombre, una serie de parámetros opcionales y un valor de retorno opcional. Pueden tener variables locales. </li></ul><ul><li>Los parámetros pueden ser por referencia (se modifica su valor, ej: “valor”) o por valor (no se altera el valor, ej: “flag”) </li></ul><ul><li>Se pueden llamar desde otros objetos </li></ul>
  19. 19. Operadores <ul><li>Operador de C/AL Significado </li></ul><ul><li>. campo de registro, control de formulario o informe </li></ul><ul><li>( ) paréntesis </li></ul><ul><li>[ ] indización </li></ul><ul><li>:: ámbito </li></ul><ul><li>+ suma </li></ul><ul><li>- resta </li></ul><ul><li>* multiplicación </li></ul><ul><li>/ división </li></ul><ul><li>DIV división entera </li></ul><ul><li>MOD resto </li></ul><ul><li>> mayor que </li></ul><ul><li>>= mayor o igual que </li></ul><ul><li>< menor que </li></ul><ul><li><= menor o igual que </li></ul><ul><li>= igual a </li></ul><ul><li><> diferente de </li></ul><ul><li>IN pertenencia a un rango (conjunto) </li></ul><ul><li>AND Y lógico </li></ul><ul><li>OR O lógico </li></ul><ul><li>NOT negación lógica </li></ul><ul><li>XOR O excluyente lógico </li></ul><ul><li>.. Rango </li></ul>
  20. 20. Operadores (II) <ul><li>1. . campo de un registro </li></ul><ul><li>[] indización </li></ul><ul><li>() paréntesis </li></ul><ul><li>:: ámbito </li></ul><ul><li>2. NOT negación lógica </li></ul><ul><li>- inversión de signo o signo negativo </li></ul><ul><li>+ signo positivo </li></ul><ul><li>3. * multiplicación </li></ul><ul><li>/ división decimal </li></ul><ul><li>DIV división entera </li></ul><ul><li>MOD resto </li></ul><ul><li>AND Y lógico </li></ul><ul><li>XOR O excluyente lógico </li></ul>4. + suma - resta OR O lógico 5. > mayor que < menor que >= mayor o igual que <= menor o igual que = igual que <> distinto de IN pertenencia a conjunto 6. .. rango Precedencia
  21. 21. Operadores (III)
  22. 22. Ejemplos <ul><li>Creamos un nuevo formulario tipo Ficha </li></ul><ul><li>Añadimos 3 TextBox y un Botón (Ejecutar) </li></ul><ul><li>Asociamos los 3 TextBox con 3 variables: </li></ul><ul><ul><li>Value1: Integer </li></ul></ul><ul><ul><li>Value2: Integer </li></ul></ul><ul><ul><li>Result: Boolean </li></ul></ul><ul><li>Al pulsar Ejecutar, realizamos las siguientes comparaciónes: </li></ul><ul><ul><li>Result := Value1 > Value2; </li></ul></ul><ul><ul><li>Result := (Value1 >= Value2) AND (Value1 <= Value2 * 2) </li></ul></ul>
  23. 23. Sentencias - Normas <ul><li>Notación y reglas </li></ul><ul><li>Una sentencia comienza en ; como separador de la anterior. </li></ul><ul><li>La parte ELSE de las sentencias IF es parte de la misma sentencia. </li></ul><ul><li>[ ] indica opcionalidad en esa parte de la sentencia. </li></ul><ul><li>Un bloque de sentencias contiene un conjunto de sentencias entre BEGIN y END; </li></ul><ul><li>Begin </li></ul><ul><li><sentencia> </li></ul><ul><li><sentencia> </li></ul><ul><li>.. </li></ul><ul><li>End; </li></ul><ul><li>Una <expresion> devuelve un valor. </li></ul><ul><li>Una <expr-asignación> asigna el valor resultado de la expresión a una variable. </li></ul><ul><li>El nombre de los campos, si contiene espacios o caracteres especiales, se escribe entre “comillas” </li></ul>
  24. 24. Sentencias (I) <ul><li>Asignación </li></ul><ul><ul><li>Operador de asignación </li></ul></ul><ul><ul><li>hoy := 220704D; </li></ul></ul><ul><ul><li>valor := 4; </li></ul></ul><ul><ul><li>Como consecuencia de llamada a función </li></ul></ul><ul><ul><li>Valor de retorno de una función </li></ul></ul><ul><ul><li>Function Cuadrado (valor : Integer) cuad : Integer </li></ul></ul><ul><ul><li>Begin </li></ul></ul><ul><ul><li>cuad := valor * valor </li></ul></ul><ul><ul><li>End; </li></ul></ul><ul><ul><li>valor := Cuadrado(3); // Valor será igual a 9 </li></ul></ul><ul><ul><li>Paso de parámetro por referencia a una función </li></ul></ul><ul><ul><li>Function Cubo (VAR valor : Integer) </li></ul></ul><ul><li>Begin </li></ul><ul><li>valor := valor * valor * valor </li></ul><ul><li>End; </li></ul><ul><li>valor := 2; </li></ul><ul><li>Cubo(valor); // Valor será igual a 8 </li></ul>
  25. 25. Operador de Cadenas <ul><li>El símbolo ‘+’ se utiliza para concatenar cadenas </li></ul><ul><ul><li>¿Qué ocurre si realizamos una asignación de una cadena con el resultado de concatenar otras dos, y dicho resultado es de mayor tamaño que la cadena? </li></ul></ul><ul><ul><ul><li>Se produce un error de ejecución </li></ul></ul></ul><ul><ul><li>Lo podemos solucionar comprobando primero dicho desbordamiento: </li></ul></ul><ul><ul><ul><li>MAXSTRLEN nos devuelve el tamaño máximo de cadena </li></ul></ul></ul><ul><ul><ul><li>COPYSTR nos permite copiar n caracteres de una cadena a otra. </li></ul></ul></ul><ul><ul><ul><li>Usar F5 (Symbol Menu) para ayudarse </li></ul></ul></ul>
  26. 26. Sentencias (II) <ul><li>Decisión </li></ul><ul><li>IF </li></ul><ul><li>Evalúa la condición, si es cierta se ejecuta el bloque de sentencias del IF , en otro caso el de la parte ELSE. </li></ul><ul><li>IF <condición> THEN </li></ul><ul><li><bloque de sentencias> </li></ul><ul><li>[ ELSE </li></ul><ul><li><bloque de sentencias> ] </li></ul><ul><li>Ejemplo </li></ul><ul><li>IF (Cantidad = 0) AND (&quot;Cantidad facturada&quot; <> 0) THEN </li></ul><ul><li>TESTFIELD(&quot;Nº orden mov. producto asoc.&quot;) </li></ul><ul><li>ELSE BEGIN </li></ul><ul><li>IF Cantidad <> &quot;Cantidad facturada&quot; THEN </li></ul><ul><li>TESTFIELD(&quot;Cantidad facturada&quot;,0); </li></ul><ul><li>TESTFIELD(&quot;Nº orden mov. producto asoc.&quot;,0); </li></ul><ul><li>END; </li></ul>
  27. 27. Ejemplos <ul><li>Modificaremos el formulario anterior de la siguiente forma: </li></ul>Definición Variables Execute OnPush Execute Clear
  28. 28. Ejemplos <ul><li>Añadir lo necesario para calcular la media de unidades vendidas/compradas </li></ul><ul><ul><li>Contadores de veces que se compra/vende </li></ul></ul><ul><ul><li>Totales de Ventas/Compras </li></ul></ul>
  29. 29. Ejemplos <ul><li>Sobre el ejemplo anterior, añadimos la opción una lista de 10 TextBox y un Array de 10 decimales </li></ul><ul><li>Debemos hacer que cada vez que pulsemos el botón Ejecutar, el resultado se muestre en una posición distinta del Array </li></ul>
  30. 30. Sentencias (III) <ul><li>Decisión </li></ul><ul><li>CASE </li></ul><ul><li>Evalúa la expresión y ejecuta el bloque de sentencias del valor correspondiente. Si no hay un valor igual, ejecuta el bloque de sentencias del ELSE . </li></ul><ul><ul><li>CASE <expresión> OF </li></ul></ul><ul><li><valor>: <bloque de sentencias>; </li></ul><ul><li>… </li></ul><ul><li><valor>: <bloque de sentencias> </li></ul><ul><li>[ ELSE <bloque de sentencias> ] </li></ul><ul><li>END; </li></ul><ul><li>Ejemplo </li></ul><ul><li>CASE &quot;Tipo importe&quot; OF </li></ul><ul><li>&quot;Tipo importe&quot;::Saldo: ValorCol := CGCta.&quot;Importe pptdo.&quot;; </li></ul><ul><li>&quot;Tipo importe&quot;::Debe : </li></ul><ul><li>BEGIN </li></ul><ul><li>ValorCol := CGCta.&quot;Debe presupuestado&quot;; </li></ul><ul><li>Debe := TRUE; </li></ul><ul><li>END </li></ul><ul><li>ELSE ValorCol := CGCta.&quot;Haber presupuestado&quot;; </li></ul><ul><li>END; </li></ul>
  31. 31. Sentencias (IV) <ul><li>Iteración </li></ul><ul><ul><li>FOR </li></ul></ul><ul><ul><li>Repite un conjunto de sentencias hasta que el resultado de la expresión de asignación alcance el valor indicado </li></ul></ul><ul><li>FOR <expr-asignación> TO/DOWNTO <valor> DO </li></ul><ul><li>< bloque de sentencias > </li></ul><ul><li>Ejemplo </li></ul><ul><li>FOR j := Tareas DOWNTO 1 DO BEGIN </li></ul><ul><li>Inclinacion[j] := Inclinacion[j - 1]; </li></ul><ul><li>Constante[j] := Constante[j - 1]; </li></ul><ul><li>MaxValImport[j] := MaxValImport[j - 1]; </li></ul><ul><li>END; </li></ul>
  32. 32. Sentencias (V) <ul><li>Iteración </li></ul><ul><ul><li>REPEAT .. UNTIL </li></ul></ul><ul><ul><li>Repite un conjunto de sentencias hasta que la expresión evalúe a TRUE . </li></ul></ul><ul><li>REPEAT </li></ul><ul><li><bloque de sentencias> </li></ul><ul><li>UNTIL <expresión> </li></ul><ul><li>Ejemplo </li></ul><ul><li>IF CGCta.FIND('-') THEN </li></ul><ul><li>REPEAT </li></ul><ul><li>Result := Result + CalcCGCta(CGCta,ColumEsqCta); </li></ul><ul><li>Num := Num + 1; </li></ul><ul><li>UNTIL CGCta.NEXT = 0; </li></ul>
  33. 33. Sentencias (VI) <ul><li>Iteración </li></ul><ul><ul><li>WHILE .. DO </li></ul></ul><ul><ul><li>Repite el conjunto de sentencias hasta el siguiente END si la expresión evalúa a TRUE </li></ul></ul><ul><ul><li>WHILE <expresión> DO </li></ul></ul><ul><li><bloque de sentencias> </li></ul><ul><li>[ END; ] </li></ul><ul><li>Ejemplo </li></ul><ul><li>LinComentVenta.SETRANGE(&quot;Tipo documento&quot;,&quot;Tipo documento&quot;); </li></ul><ul><li>LinComentVenta.SETRANGE(&quot;Nº&quot;,&quot;Nº&quot;); </li></ul><ul><li>WHILE LinComentVenta.FIND('-') DO BEGIN </li></ul><ul><li>LinComentVenta.DELETE; </li></ul><ul><li>LinComentVenta.&quot;Tipo documento&quot; := CabVta.&quot;Tipo documento&quot;; </li></ul><ul><li>LinComentVenta.&quot;Nº&quot; := CabVta.&quot;Nº&quot;; </li></ul><ul><li>LinComentVenta.INSERT; </li></ul><ul><li>END; </li></ul>
  34. 34. Sentencias (y VII) <ul><li>Otras </li></ul><ul><ul><li>WITH </li></ul></ul><ul><ul><li>Las sentencias dentro del WITH se refieren al registro, formulario, etc. </li></ul></ul><ul><li>WITH Clie DO BEGIN </li></ul><ul><li>&quot;No.&quot; := '1'; </li></ul><ul><li>Name := 'Pepe'; </li></ul><ul><li>Address := 'Rue del Percebe, nº 13'; </li></ul><ul><li>City := 'Niu llor'; </li></ul><ul><li>END; </li></ul><ul><ul><li>EXIT </li></ul></ul><ul><ul><li>Salir de un segmento de código o función (se puede retornar el valor) </li></ul></ul><ul><li>FUNCTION Absoluto (valor : Integer) ret : Integer </li></ul><ul><li>BEGIN </li></ul><ul><li>IF (valor < 0) THEN BEGIN </li></ul><ul><li>ret := -valor; </li></ul><ul><li>EXIT; </li></ul><ul><li>END; </li></ul><ul><li>EXIT(valor); </li></ul><ul><li>END; </li></ul>
  35. 35. Comentarios <ul><li>// - Una sola línea </li></ul><ul><li>// Find next register no. </li></ul><ul><li>IF ItemReg.FIND(‘+’) THEN </li></ul><ul><li>ItemRegNo := ItemReg.”Nº” + 1 </li></ul><ul><li>ELSE </li></ul><ul><li>ItemRegNo := 1; </li></ul><ul><li>{} – Varias líneas </li></ul><ul><li>{ Find next </li></ul><ul><li> register no. } </li></ul><ul><li>IF ItemReg.FIND(‘+’) THEN </li></ul><ul><li>ItemRegNo := ItemReg.”Nº” + 1 </li></ul><ul><li>ELSE </li></ul><ul><li>ItemRegNo := 1; </li></ul>
  36. 36. Ejemplos <ul><li>Modificar el formulario para que se muestren siempre los 10 últimos resultados, mostrando siempre en la primera posición (la superior) el último resultado obtenido </li></ul>
  37. 37. Ejemplos <ul><li>Añadimos al formulario un botón “Ordenar” y realizaremos una ordenación de los elementos a través del método de la burbuja </li></ul>
  38. 38. Ejemplos Repetimos la ordenación hasta que el array está ordenado
  39. 39. Ejemplos Mejoramos el Algoritmo sabiendo que en cada iteración el último elemento siempre queda “ordenado”
  40. 40. Tablas <ul><li>Dónde introducir código </li></ul><ul><ul><li>Disparadores de tabla: </li></ul></ul><ul><ul><li>OnInsert . Se ejecuta al insertar un registro en la tabla. </li></ul></ul><ul><ul><li>OnModify . Se ejecuta al modiicar un campo de un registro de la tabla. </li></ul></ul><ul><ul><li>OnDelete . Se ejecuta al borrar un registro de la tabla. </li></ul></ul><ul><ul><li>OnRename . Se ejecuta al modificar un campo que forma parte de la clave primaria de un registro de la tabla. </li></ul></ul><ul><ul><li>Disparadores de campos en la tabla: </li></ul></ul><ul><ul><li>OnValidate . Se ejecuta al modificar el valor de un campo. </li></ul></ul><ul><ul><li>OnLookup . Se ejecuta al pulsar F6 sobre el campo para buscar un valor. </li></ul></ul><ul><ul><li>Funciones definidas en una tabla </li></ul></ul><ul><ul><li>Se pueden definir funciones en la tabla y llamarlas desde el propio objeto o desde otros objetos. </li></ul></ul>
  41. 41. Tablas (II) <ul><li>Para acceder a tablas se definen variables tipo Record </li></ul><ul><li>Modificación de campos de la tabla: </li></ul><ul><ul><li>Mediante asignación de valores. </li></ul></ul><ul><ul><li>Clie : Record (Cliente); // Variable Clie de tipo registro de // tabla cliente </li></ul></ul><ul><ul><li>Clie.”Nº” := ‘10’; </li></ul></ul><ul><ul><li>Clie.”Nombre” := ‘Pepe’; </li></ul></ul><ul><ul><li>Con la sentencia Validate, asigna el valor y ejecuta el disparador OnValidate de cada campo asignado de la tabla Cliente. </li></ul></ul><ul><ul><li>Clie : Record (Cliente); </li></ul></ul><ul><ul><li>Clie.VALIDATE(“Nº”, ’10’); </li></ul></ul><ul><ul><li>Clie.VALIDATE(Nombre, ‘Pepe’); </li></ul></ul>
  42. 42. Tablas (III) <ul><li>Trabajo con registros de la tabla: </li></ul><ul><ul><li>Inicialización. Se establece cada campo del registro a su valor por defecto dependiendo de su tipo. </li></ul></ul><ul><ul><li>Clie : Record (Cliente); </li></ul></ul><ul><ul><li>Clie.INIT; </li></ul></ul><ul><ul><li>Inserción de registros en la tabla. Con la sentencia INSERT y un párámetro que indica si se ejecuta el disparador OnInsert de la tabla o no. </li></ul></ul><ul><ul><li>Clie.VALIDATE(“Nº”, ‘10’); </li></ul></ul><ul><ul><li>Clie.VALIDATE(Nombre, ‘Pepe’); </li></ul></ul><ul><ul><li>… </li></ul></ul><ul><ul><li>Clie.INSERT(TRUE); </li></ul></ul><ul><ul><li>Modificación de registros de la tabla. Sentencia MODIFY con un parámetro que indica si se ejecuta el disparador OnModify de la tabla. </li></ul></ul><ul><ul><li>Clie.VALIDATE(Nombre, ‘Luis’); </li></ul></ul><ul><ul><li>Clie.MODIFY(TRUE); </li></ul></ul>
  43. 43. Tablas (IV) <ul><li>Trabajo con registros de la tabla: </li></ul><ul><ul><li>Borrado de registros en la tabla. Con la sentencia DELETE y un párámetro que indica si se ejecuta el disparador OnDelete de la tabla o no. </li></ul></ul><ul><ul><li>Clie.DELETE(TRUE); </li></ul></ul><ul><ul><li>Modificación de campos de registros de la tabla que forman parte de la clave primaria. Sentencia RENAME con un parámetro que indica si se ejecuta el disparador OnRename de la tabla. </li></ul></ul><ul><ul><li>Clie.VALIDATE(“Nº”, ‘21’); </li></ul></ul><ul><ul><li>Clie.RENAME(TRUE); </li></ul></ul>
  44. 44. Tablas (V) <ul><li>Buscar registros en una tabla: </li></ul><ul><ul><li>Obtener un registro de una tabla por su clave primaria. </li></ul></ul><ul><ul><li>Job.GET(“Job Nº.”); </li></ul></ul><ul><ul><li>Job.TESTFIELD(Blocked,FALSE); </li></ul></ul><ul><ul><li>Con las sentencias anteriores obtenemos un registro de la tabla Job (sentencia GET) y comprobamos que uno de los campos del registro tenga el valor indicado (TESTFIELD) </li></ul></ul><ul><ul><li>Obtener uno o varios registro de una tabla por claves secundarias. </li></ul></ul><ul><li>Clie.RESET; </li></ul><ul><li>Clie.SETCURRENTKEY(Name,Address,City); </li></ul><ul><li>Clie.SETRANGE(Name,’Luis’); </li></ul><ul><li>Clie.SETFILTER(City, ‘%1 | %2’, ‘Paris’, ‘Roma’); </li></ul><ul><ul><li>Primero actuamos sobre la variable Clie eliminando todos los filtros y rangos anteriores ( RESET ). Activamos una clave por la que vamos a buscar ( SETCURRENTKEY ) para que el establecimiento de rangos y la búsqueda de registro se base en índices para esta clave y sea mucho más efectiva. Se establecen filtros con dos funciones: SETRANGE y SETFILTER , básicamente iguales aunque SETFILTER permite establecer condiciones más complejas. Es recomendable establecer los filtros sobre los campos en el orden en que aparecen en la clave primaria. En este momento Clie contiene todos los registros que cumplen las condiciones indicadas en los filtros. </li></ul></ul>
  45. 46. Tablas (VI) <ul><li>Buscar registros en una tabla: </li></ul><ul><ul><li>Recorrer registros de la tabla </li></ul></ul><ul><ul><li>IF Clie.FIND(‘-’) THEN </li></ul></ul><ul><ul><li>REPEAT </li></ul></ul><ul><ul><li><tratar registro> </li></ul></ul><ul><ul><li>UNTIL Clie.NEXT = 0; </li></ul></ul><ul><li>FIND permite obtener un registro dentro del filtro establecido. El parámetro indica: </li></ul><ul><li>‘ -’ toma el primer registro del filtro </li></ul><ul><li>‘ +’ toma el último registro del filtro </li></ul><ul><li>‘ =‘ toma el registro que es igual a los valores de las claves (por defecto) </li></ul><ul><li>FIND devuelve un valor Boolean y si no encuentra un registro que cumpla con las condiciones del filtro provoca un error de ejecución. Para evitar este error se puede encerrar en una sentencia IF . </li></ul><ul><li>NEXT toma el siguiente registro del filtro y toma un parámetro: </li></ul><ul><li>> 0 busca el siguiente registro saltando el número indicado </li></ul><ul><li>< 0 busca el registro anterior saltando el número indicado </li></ul><ul><li>Por defecto busca el siguiente registro. </li></ul>
  46. 47. Tablas (y VII) <ul><li>OnLookup : </li></ul><ul><ul><li>Buscar valores de campos en otra tabla. </li></ul></ul><ul><ul><li>Clie.SETCURRENTKEY(&quot;Nº&quot;); </li></ul></ul><ul><ul><li>Clie.SETFILTER(&quot;Nº&quot;,'<40000'); </li></ul></ul><ul><ul><li>ListaClientes.SETTABLEVIEW(Clie); </li></ul></ul><ul><ul><li>IF Clie.FIND('-') THEN; </li></ul></ul><ul><ul><li>ListaClientes.SETRECORD(Clie); </li></ul></ul><ul><ul><li>ListaClientes.LOOKUPMODE(TRUE); </li></ul></ul><ul><ul><li>IF ListaClientes.RUNMODAL = ACTION::LookupOK THEN BEGIN </li></ul></ul><ul><ul><li>ListaClientes.GETRECORD(Clie); </li></ul></ul><ul><ul><li>cliente.validate(Clie.&quot;Nº “); </li></ul></ul><ul><ul><li>CLEAR(ListaClientes); </li></ul></ul><ul><ul><li>END; </li></ul></ul><ul><ul><li>Se establecen los filtros adecuados en la variable Clie de tipo Record de Clientes . </li></ul></ul><ul><ul><li>ListaClientes es de tipo Form . </li></ul></ul><ul><ul><li>SETTABLEVIEW establece la vista del formulario ListaClientes y establecemos el cliente activo mediante SETRECORD . </li></ul></ul><ul><ul><li>LOOKUPMODE hace que el formulario sirva para obtener datos. </li></ul></ul><ul><ul><li>GETRECORD obtiene el registro marcado. </li></ul></ul><ul><ul><li>CLEAR elimina el formulario. </li></ul></ul>
  47. 48. Acceso a Objetos <ul><li>Form: </li></ul><ul><ul><li>Acceso directo: </li></ul></ul><ul><ul><ul><li>CGMov.FIND(‘-’); </li></ul></ul></ul><ul><ul><ul><li>FORM.RUN (FORM::&quot;Movs. contabilidad&quot;, CGMov); </li></ul></ul></ul><ul><ul><li>IF FORM.RUNMODAL (216, LinPptoProy)=ACTION::LookupOK THEN BEGIN </li></ul></ul><ul><ul><li>&quot;Cód. fase&quot; := LinPptoProy.&quot;Cód. fase&quot;; </li></ul></ul><ul><ul><li>&quot;Cód. subfase&quot; := LinPptoProy.&quot;Cód. subfase&quot;; </li></ul></ul><ul><ul><li>&quot;Cód. tarea&quot; := LinPptoProy.&quot;Cód. tarea&quot;; </li></ul></ul><ul><ul><li>END; </li></ul></ul><ul><ul><li>Acceso con variable: </li></ul></ul><ul><li>ListCtaCG : Form “Lista de Cuentas”; </li></ul><ul><li>ListCtaCG.LOOKUPMODE(TRUE); </li></ul><ul><li>IF ListCtaCG. RUNMODAL = ACTION::LookupOK THEN … </li></ul><ul><li>FormNavegar : Form “Navegar”; </li></ul><ul><li>FormNavegar.DefDoc(&quot;Fecha registro&quot;,&quot;Nº&quot;); </li></ul><ul><li>FormNavegar. RUN ; </li></ul>
  48. 49. Acceso a Objetos (II) <ul><li>Report: </li></ul><ul><ul><li>Acceso directo: </li></ul></ul><ul><li>Clie.SETRECFILTER; // coloca un filtro (en la clave primaria) // para que el Report se ejecute sólo sobre el // Cliente actual </li></ul><ul><li>REPORT. RUN (REPORT::&quot;Inf-ejemplo&quot;,ReqForm,FALSE,Clie); </li></ul><ul><li>REPORT. RUNMODAL (REPORT::&quot;Inf-ejemplo&quot;,FALSE,Impresora,Clie); </li></ul><ul><ul><li>Acceso con variable: </li></ul></ul><ul><li>r : Report “Proveedor - Líneas pedidos”; </li></ul><ul><li>CLEAR(r); </li></ul><ul><li>Prov.SETRECFILTER; </li></ul><ul><li>r.SETTABLEVIEW(Prov); </li></ul><ul><li>r. RUNMODAL ; </li></ul><ul><li>… </li></ul><ul><li>r. RUN ; </li></ul>
  49. 50. Acceso a Objetos (y III) <ul><li>Codeunit: </li></ul><ul><li>Se ejecuta la función RUN de la Codeunit </li></ul><ul><ul><li>Acceso directo: </li></ul></ul><ul><ul><li>IF CODEUNIT. RUN (22, LinDiaProd) THEN </li></ul></ul><ul><ul><li>… </li></ul></ul><ul><ul><li>Acceso con variable: </li></ul></ul><ul><ul><li>DiaGenTestLin : Codeunit “Dia. Gen-Test línea”; </li></ul></ul><ul><ul><li>DiaGenTestLin. RUN (LinDiaGen); </li></ul></ul><ul><ul><li>// LinDiaGen puede haber cambiado (VAR) </li></ul></ul>
  50. 51. Formularios <ul><li>Dónde introducir código </li></ul><ul><ul><li>Disparadores de formulario: </li></ul></ul><ul><ul><li>OnAfterGetRecord . Se ejecuta al recurperar un registro de la tabla, pero antes de mostrar el registro en el formulario. </li></ul></ul><ul><ul><li>Disparadores de controles del formulario. </li></ul></ul><ul><ul><li>Botones del formulario: </li></ul></ul><ul><ul><ul><li>Command Button </li></ul></ul></ul><ul><ul><ul><li>Menu Button: En cada uno de los Menu Items </li></ul></ul></ul><ul><ul><li>Funciones definidas en un formulario: </li></ul></ul><ul><ul><li>Se pueden definir funciones en el formulario y llamarlas desde el propio objeto o desde otros objetos. </li></ul></ul><ul><ul><li>¡¡ En cualquier caso el código debe escribirse en la tabla si es posible !! </li></ul></ul>
  51. 52. Formularios (II) <ul><ul><li>Después de recuperar un registro de la tabla, pero antes de mostrarlo en el formulario se ejecuta el trigger OnAfterGetRecord. </li></ul></ul><ul><ul><li>Ej: Antes de mostrar el registro, aplica un filtro y calcula el valor de un SumIndexField que mostrará en el formulario. </li></ul></ul>
  52. 53. Formularios (y III) <ul><ul><li>Al pulsar sobre un botón o sobre una opción de menú en un botón desplegable se ejecuta el trigger OnPush . </li></ul></ul><ul><ul><li>Ej: Podemos invocar una función en un Codeunit con un parámetro. </li></ul></ul>
  53. 54. Informes <ul><li>Dónde introducir código </li></ul><ul><ul><li>Disparadores del informe: </li></ul></ul><ul><ul><li>OnPreReport . Se procesa antes de ejecutar el informe. </li></ul></ul><ul><ul><li>Disparadores de cada DataItem: </li></ul></ul><ul><ul><li>OnPreDataItem. Se ejecuta antes de que el DataItem se procese. </li></ul></ul><ul><ul><li>OnAfterGetRecord. Se ejecuta cada vez que se obtiene un registro. </li></ul></ul><ul><ul><li>OnPostDataItem. Se ejecuta después del procesamiento del DataItem. </li></ul></ul><ul><ul><li>Request Form del informe. </li></ul></ul><ul><ul><li>Funciones definidas en un informe. </li></ul></ul><ul><ul><li>Secciones del formulario: </li></ul></ul><ul><ul><li>OnPreSection. Se ejecuta antes de que una sección sea procesada. </li></ul></ul><ul><ul><li>OnPostSection. Se ejecuta después de que una sección sea procesada. </li></ul></ul>
  54. 55. Informes (II) <ul><ul><li>Antes de la ejecución de un Report se ejecuta el trigger OnPreReport. </li></ul></ul><ul><ul><li>Ej: Podemos utilizarlo para obtener los filtros con los que el Report se ha llamado, y después imprimirlos en una sección Header (FiltClient y FiltMovProducto) . </li></ul></ul>
  55. 56. Informes (III) <ul><ul><li>Antes de la ejecución de un DataItem se dispara OnPreDataItem. </li></ul></ul><ul><ul><li>Ej: Podemos utilizarlo para mantener totales de un campo para una sección FOOTER con la función CREATETOTALS . También se puede establecer un registro por página con la función NEWPAGEPERRECORD , si hemos elegido la opción en el Request Form. </li></ul></ul><ul><ul><li>Después de obtener un registro del DataItem se ejecuta OnAfterGetRecord . </li></ul></ul><ul><ul><li>Ej: Podemos hacer cálculos para algún valor que se va a imprimir ( Bfº bruto ). También se puede llamar a NEWPAGE desde este disparador para cambiar de página. </li></ul></ul><ul><ul><li>Con CurrReport.SKIP podemos saltar la iteración del DataItem. </li></ul></ul>
  56. 57. Informes (y IV) <ul><ul><li>Antes de mostrar una sección se ejecuta OnPreSection. </li></ul></ul><ul><ul><li>Ej: Podemos querer mostrar la sección o no (con la función SHOWOUTPUT ), o realizar los cáculos para un valor a imprimir. </li></ul></ul>
  57. 58. Unidades de Código <ul><li>Donde introducir código </li></ul><ul><ul><li>Función OnRun : </li></ul></ul><ul><ul><li>Se crea por defecto con una nueva codeunit. </li></ul></ul><ul><ul><li>Es el punto de entrada para su ejecución por defecto . </li></ul></ul><ul><ul><li>Puede tener como parámetro una registro. </li></ul></ul><ul><ul><li>Funciones de la Codeunit: </li></ul></ul><ul><ul><li>Si se crea una función en una Codeunit se puede invocar desde otros objetos: </li></ul></ul><ul><ul><li>DiaGenTestLin : Codeunit “Dia. Gen-Test línea”; </li></ul></ul><ul><ul><li>DiaGenTestLin. Testear (LinDiaGen); </li></ul></ul>
  58. 59. DataPorts <ul><li>Dónde introducir código </li></ul><ul><ul><li>Disparadores de DataPort </li></ul></ul><ul><ul><ul><li>OnPreDataport . Antes de la ejecución del Dataport. </li></ul></ul></ul><ul><ul><ul><li>OnPostDataport. Después de la ejecución del Dataport. </li></ul></ul></ul><ul><ul><li>Disparadores de DataItem </li></ul></ul><ul><ul><ul><li>OnPreDataItem(). Antes de procesar el DataItem. </li></ul></ul></ul><ul><ul><ul><li>OnBeforeExportRecord(). Antes de exportar un registro. </li></ul></ul></ul><ul><ul><ul><li>OnAfterExportRecord(). Después de exportar un registro. </li></ul></ul></ul><ul><ul><ul><li>OnBeforeImportRecord(). Antes de importar un registro. </li></ul></ul></ul><ul><ul><ul><li>OnAfterImportRecord(). Después de importar un registro. </li></ul></ul></ul><ul><ul><ul><li>OnPostDataItem(). Después de procesar un DataItem. </li></ul></ul></ul><ul><ul><li>Request form del Dataport </li></ul></ul><ul><ul><li>Funciones del Dataport </li></ul></ul>
  59. 60. DataPorts (II) <ul><ul><li>Un Dataport puede importar datos y añadirlos a una tabla. </li></ul></ul><ul><ul><li>Ej: Añade registros a un diario. Necesita antes de comenzar saber que “Nº línea” tendrá el primer registro de los importados </li></ul></ul>
  60. 61. DataPorts (III) <ul><ul><li>Antes de procesar un DataItem. </li></ul></ul><ul><ul><li>Ej: Una función del Dataport es llamada antes de invocar el Dataport, después se ejecuta el Dataport y se establece un filtro sobre un campo del DataItem. La variable FiltroNoCampaña no pierde su valor entre las dos llamadas si no se ejecuta CLEAR . </li></ul></ul>
  61. 62. DataPorts (y IV) <ul><ul><li>Después de importar el registro, y antes de insertarlo en la tabla. </li></ul></ul><ul><ul><li>Ej: Se busca un nuevo número de línea y se rellenan los campo de descripción. </li></ul></ul>
  62. 63. Funciones Comunes <ul><li>Otras funciones comunmente usadas: </li></ul><ul><ul><li>RESET </li></ul></ul><ul><ul><ul><li>Elimina filtros y selección de claves en una variable Record </li></ul></ul></ul><ul><ul><ul><li>DesdeLMComp.RESET; </li></ul></ul></ul><ul><ul><ul><li>DesdeLMComp.SETRANGE(&quot;Nº L.M.&quot;,&quot;Nº producto&quot;); </li></ul></ul></ul><ul><ul><ul><li>DesdeLMComp.SETRANGE(Tipo,DesdeLMComp.Tipo::Producto); </li></ul></ul></ul><ul><ul><ul><li>DesdeLMComp.FIND('-'); </li></ul></ul></ul><ul><ul><ul><li>NoSigLin := &quot;Nº línea“ </li></ul></ul></ul><ul><ul><li>COUNT </li></ul></ul><ul><ul><ul><li>Cuenta el número de registros. </li></ul></ul></ul><ul><ul><ul><li>DesdeLMComp.SETRANGE(&quot;Nº L.M.&quot;,&quot;Nº producto&quot;); </li></ul></ul></ul><ul><ul><ul><li>DesdeLMComp.SETRANGE(Tipo,DesdeLMComp.Tipo::Producto); </li></ul></ul></ul><ul><ul><ul><li>NoCompoLM := DesdeLMComp.COUNT; </li></ul></ul></ul>
  63. 64. Funciones Comunes (II) <ul><ul><li>COPY </li></ul></ul><ul><ul><ul><li>Copia una variable Record en otra de la misma tabla, incluyendo filtros y claves activas. </li></ul></ul></ul><ul><ul><ul><li>NuevoClie := COPY(Clie); </li></ul></ul></ul><ul><ul><li>TRANSFERFIELDS </li></ul></ul><ul><ul><ul><li>Copia el contenido de los campos de una variable Record a otra que no tiene que ser de la misma tabla. Tiene en cuenta para ello el número de campo.Los campos que no tengan correspondencia de número se establecen a sus valores por defecto. </li></ul></ul></ul><ul><ul><ul><li>RegHasta := TRANSFERFIELDS (RegDesde); </li></ul></ul></ul>
  64. 65. Funciones Comunes (III) <ul><ul><li>EVALUATE </li></ul></ul><ul><ul><ul><li>Convertir un valor de un tipo a otro. </li></ul></ul></ul><ul><li>Valor := '010196'; // Tipo cadena </li></ul><ul><li>Ok := EVALUATE(VarInteger, Valor); // Entero = 10196 </li></ul><ul><li>Ok := EVALUATE(VarDate, Valor); // Fecha = 010196D </li></ul><ul><ul><li>FORMAT </li></ul></ul><ul><ul><ul><li>Convertir un valor en una cadena de texto </li></ul></ul></ul><ul><ul><ul><li>MESSAGE('Today is %1', FORMAT(TODAY )); </li></ul></ul></ul>
  65. 66. Funciones Comunes (IV) <ul><ul><li>DELETEALL </li></ul></ul><ul><ul><ul><li>Borrar una serie de registros de una tabla </li></ul></ul></ul><ul><li>LinExtCtaBanco.SETRANGE(&quot;Nº banco&quot;,&quot;Nº banco&quot;); </li></ul><ul><li>LinExtCtaBanco.SETRANGE(&quot;Nº extracto&quot;,&quot;Nº extracto&quot;); </li></ul><ul><li>LinExtCtaBanco.DELETEALL; </li></ul><ul><ul><li>MODIFYALL </li></ul></ul><ul><ul><ul><li>Modificar una serie de registros de una tabla </li></ul></ul></ul><ul><li>CGMov.RESET; </li></ul><ul><li>CGMov.SETCURRENTKEY(&quot;Nº cuenta&quot;); </li></ul><ul><li>CGMov.SETRANGE(&quot;Nº cuenta&quot;,CGCta.&quot;Nº&quot;); </li></ul><ul><li>CGMov.MODIFYALL(&quot;Nº cuenta&quot;,''); </li></ul>
  66. 67. Funciones Comunes (V) <ul><ul><li>COPYFILTER[S] </li></ul></ul><ul><ul><ul><li>Copia los filtros de una variable a otra de tipo Record de la misma tabla (COPYFILTER sólo copia los filtros de un campo) </li></ul></ul></ul><ul><ul><ul><li>Clie1.SETFILTER(&quot;No.&quot;, '<1000'); </li></ul></ul></ul><ul><ul><ul><li>Clie1.SETRANGE(Group, 1); </li></ul></ul></ul><ul><ul><ul><li>Clie2.COPYFILTERS(Clie1); </li></ul></ul></ul><ul><ul><li>TESTFIELD </li></ul></ul><ul><ul><ul><li>Comprueba que un campo contenga un valor dado. Si se omite el segundo parámetro se comprueba que contenga un valor distinto de 0 o blanco. Si esto no se cumple muestra un mensaje de ERROR. </li></ul></ul></ul><ul><ul><ul><li>Clie.TESTFIELD(Bloqueado, FALSE); </li></ul></ul></ul><ul><ul><ul><li>Clie.TESTFIELD(“Nº”); </li></ul></ul></ul>
  67. 68. Funciones Comunes (VI) <ul><li>LOCKTABLE </li></ul><ul><ul><li>Bloquea una tabla para procesos transaccionales </li></ul></ul><ul><li>FIELDNAME </li></ul><ul><ul><li>Obtiene el nombre de un campo </li></ul></ul>
  68. 69. SumIndexFields <ul><li>[Ok :=] Record. CALCSUMS (Campo1 [, Campo2] ,...) </li></ul><ul><li>Devuelve la suma para uno (o varios) SumIndexField. </li></ul><ul><li>Debe estar activa una clave para la que se haya definido el campo. </li></ul><ul><li>No debe haber filtros en campos fuera de la clave. </li></ul><ul><li>Si no se cumplen las condiciones anteriores, se devuelve FALSE (o se lanza un mensaje de error). </li></ul><ul><li>MovCli.SETCURRENTKEY(&quot;Nº cliente&quot;,&quot;Fecha registro&quot;); </li></ul><ul><li>MovCli.SETRANGE(&quot;Nº cliente&quot;, 'AAA 1050'); </li></ul><ul><li>MovCli.SETRANGE(&quot;Fecha registro&quot;, 010199D, 123102D); </li></ul><ul><li>MovCli.CALCSUMS(Importe); </li></ul>
  69. 70. FlowFields <ul><li>[Ok :=] Record. CALCFIELDS (Campo1 [, Campo2] ,...) </li></ul><ul><li>Los campos de una tabla que son FlowFields se deben recalcular al aplicar filtros. </li></ul><ul><li>Cliente.SETRANGE(&quot;Filtro fechas&quot;,010199D,123102D); </li></ul><ul><li>Cliente.CALCFIELDS(Saldo,&quot;Saldo periodo&quot;); </li></ul>
  70. 71. Diálogos <ul><li>Mensajes: </li></ul><ul><ul><li>Mensaje asíncrono </li></ul></ul><ul><ul><ul><li>MESSAGE </li></ul></ul></ul><ul><ul><ul><li>MESSAGE('Mensaje asíncrono informativo'); </li></ul></ul></ul><ul><ul><ul><li>ERROR. Provoca Rollback. </li></ul></ul></ul><ul><ul><ul><li>ERROR('ERROR: Esto ejecuta RollBack'); </li></ul></ul></ul><ul><ul><ul><li>. FIELDERROR. Muestra el Campo Erroneo </li></ul></ul></ul><ul><ul><li>Mensaje + Ok/Cancel </li></ul></ul><ul><ul><ul><li>CONFIRM </li></ul></ul></ul><ul><ul><ul><li>IF CONFIRM ('Desea continuar') THEN </li></ul></ul></ul><ul><ul><ul><li><Código para SI> </li></ul></ul></ul><ul><ul><ul><li>ELSE </li></ul></ul></ul><ul><ul><ul><li><Código para NO> </li></ul></ul></ul>
  71. 72. Diálogos (II) <ul><li>Mensajes: </li></ul><ul><ul><li>Menú de opciones </li></ul></ul><ul><ul><ul><li>STRMENU </li></ul></ul></ul><ul><ul><ul><li>CASE STRMENU('&Insertar,&Modificar,&Borrar,&Terminar' ,4) OF </li></ul></ul></ul><ul><ul><ul><li>1: <Código para Insertar> </li></ul></ul></ul><ul><ul><ul><li>2: <Código para Modificar> </li></ul></ul></ul><ul><ul><ul><li>3: <Código para Borrar> </li></ul></ul></ul><ul><ul><ul><li>4: <Código para Terminar> </li></ul></ul></ul><ul><ul><ul><li>ELSE <Código para Cancelar> </li></ul></ul></ul><ul><ul><ul><li>END; </li></ul></ul></ul>
  72. 73. Diálogos (y III) <ul><li>Mensajes: </li></ul><ul><ul><li>Objetos de tipo Dialog </li></ul></ul><ul><ul><ul><li>Ventana.OPEN('#1############################apos;+ </li></ul></ul></ul><ul><ul><ul><li>'Cliente #2###############apos;+ </li></ul></ul></ul><ul><ul><ul><li>'Nuevo Nombre #3###############apos;+ </li></ul></ul></ul><ul><ul><ul><li>'@4@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'); </li></ul></ul></ul><ul><li>Num := 0; </li></ul><ul><li>Ventana.UPDATE(1, 'Tabla Cliente'); </li></ul><ul><li>Clie.RESET; </li></ul><ul><li>IF Clie.FIND('-') THEN </li></ul><ul><li>REPEAT </li></ul><ul><li>Num := Num + 1; </li></ul><ul><li>Ventana.UPDATE(2, Clie.Nombre); </li></ul><ul><li>Ventana.INPUT(3, NuevoNombre); </li></ul><ul><li>Ventana.UPDATE(4, ROUND((Num / Clie.COUNT) * 10000)); </li></ul><ul><li>Clie.VALIDATE(“Nombre”, NuevoNombre); </li></ul><ul><li>UNTIL Clie.NEXT = 0; </li></ul><ul><li>Ventana.CLOSE; </li></ul>
  73. 74. Acceso a Ficheros <ul><li>Escribir en un fichero: </li></ul><ul><li>IF NombArch = '' THEN </li></ul><ul><li>ERROR('Introduzca el nombre del archivo.'); </li></ul><ul><li>CLEAR(FicMovCont); </li></ul><ul><li>FicMovCont.TEXTMODE := TRUE; </li></ul><ul><li>FicMovCont.WRITEMODE := TRUE; </li></ul><ul><li>FicMovCont.QUERYREPLACE := TRUE; </li></ul><ul><li>FicMovCont.CREATE(NombArch); </li></ul><ul><li>FicMovCont.WRITE( </li></ul><ul><li>STRSUBSTNO( </li></ul><ul><li>'#1#################### #2####### #3####### #4#', </li></ul><ul><li> COMPANYNAME, FechInicConsol, FechFinConsol, FORMAT(TransfPorDia,0,2))); </li></ul><ul><li>FicMovCont.CLOSE; </li></ul>
  74. 75. Acceso a Ficheros (y II) <ul><li>Leer de un fichero: </li></ul><ul><li>IF NombArch = '' THEN </li></ul><ul><li>ERROR('Introduzca el nombre del archivo.'); </li></ul><ul><li>CLEAR(FicMovCont); </li></ul><ul><li>FicMovCont.TEXTMODE := TRUE; </li></ul><ul><li>FicMovCont.OPEN(NombArch); </li></ul><ul><li>WHILE FicMovCont.POS <> FicMovCont.LEN DO BEGIN </li></ul><ul><li>FicMovCont.READ(LinTexto); </li></ul><ul><li>CodEmpr.“Nombre&quot; := DELCHR(COPYSTR(LinTexto,1,30),'>'); </li></ul><ul><li>EVALUATE(FechInicConsol,COPYSTR(LinTexto,32,9)); </li></ul><ul><li>EVALUATE(FechFinConsol,COPYSTR(LinTexto,42,9)); </li></ul><ul><li>EVALUATE(TransfPorDia,COPYSTR(LinTexto,52,3)); </li></ul><ul><li>END; </li></ul><ul><li>FicMovCont.CLOSE; </li></ul>
  75. 76. Constantes Globales del Sistema <ul><li>USERID . Identificador del usuario que inició la sesión </li></ul><ul><li>Str := USERID; </li></ul><ul><li>COMPANYNAME . Devuelve la empresa actual </li></ul><ul><li>Str := COMPANYNAME; </li></ul><ul><li>OSVERSION. Devuelve una cadena indicando el sistema operativo actual </li></ul><ul><li>Str := OSVERSION; </li></ul><ul><li>WORKDATE . Devuelve la fecha de trabajo actual </li></ul><ul><li>TFecha := WORKDATE; </li></ul><ul><li>TODAY . Devuelve la fecha del sistema operativo </li></ul><ul><li>Fecha := TODAY; </li></ul><ul><li>TIME . Devuelve la hora del sistema operativo </li></ul><ul><li>Hora := TIME; </li></ul>
  76. 77. Funciones de Cadena <ul><li>STRPOS </li></ul><ul><ul><li>Posición de una cadena en otra </li></ul></ul><ul><li>COPYSTR </li></ul><ul><ul><li>Copia una cadena en otra </li></ul></ul><ul><li>STRLEN </li></ul><ul><ul><li>Tamaño de una Cadena </li></ul></ul><ul><li>PADSTR </li></ul><ul><ul><li>Añade caracteres a una Cadena </li></ul></ul><ul><li>MAXSTRLEN </li></ul><ul><ul><li>Tamaño máximo de una Cadena </li></ul></ul><ul><li>LOWERCASE/UPPERCAS </li></ul><ul><ul><li>Conversión a Mayúsculas/Minúsculas </li></ul></ul>
  77. 78. Funciones de Cadena (II) <ul><li>CONVERTSTR </li></ul><ul><ul><li>Sustituye un conjunto de caracteres por otro, dentro de una cadena </li></ul></ul><ul><li>DELCHR </li></ul><ul><ul><li>Borra un conjunto de caracteres dentro de una cadena </li></ul></ul><ul><li>INCSTR </li></ul><ul><ul><li>Incrementa numéricamente una cadena </li></ul></ul><ul><li>SELECTSTR </li></ul><ul><ul><li>Devuelve una subcadena, dentro de un conjunto de cadenas separadas por comas </li></ul></ul><ul><li>STRCHECKSUM </li></ul><ul><ul><li>Calcula un Checksum para una cadena de números </li></ul></ul>
  78. 79. Funciones de Fecha <ul><li>DATE2DMY </li></ul><ul><ul><li>Obtiene Día, Mes o Año de una Fecha </li></ul></ul><ul><li>DATE2DWY </li></ul><ul><ul><li>Obtiene Día, Semana o Año de una Fecha </li></ul></ul><ul><li>CALCDATE </li></ul><ul><ul><li>Calcula una Fecha en Base a una Fecha inicial y una DateFormula </li></ul></ul>
  79. 80. Funciones de Fecha (II) <ul><li>NORMALDATE </li></ul><ul><ul><li>Convierte una Fecha a “Normal Date” </li></ul></ul><ul><li>CLOSINGDATE </li></ul><ul><ul><li>Convierte una Fecha a “Closing Date” </li></ul></ul>
  80. 81. Funciones Matemáticas <ul><li>ABS </li></ul><ul><ul><li>Valor Absoluto </li></ul></ul><ul><li>POWER </li></ul><ul><ul><li>Potencia </li></ul></ul><ul><li>ROUND </li></ul><ul><ul><li>Redondeo </li></ul></ul><ul><li>RANDOMIZE </li></ul><ul><ul><li>Planta una semilla para el generador de números aleatorios </li></ul></ul><ul><li>RANDOM </li></ul><ul><ul><li>Obtiene un número aleatorio </li></ul></ul>
  81. 82. Funciones de Arrays <ul><li>ARRAYLEN </li></ul><ul><ul><li>Devuelve el número de elementos de un array </li></ul></ul><ul><li>COMPRESSARRAY </li></ul><ul><ul><li>Elimina elementos blancos de un array de texto </li></ul></ul><ul><li>COPYARRAY </li></ul><ul><ul><li>Copia elementos de un array a otro </li></ul></ul>
  82. 83. Ejemplos <ul><li>Modificar el código del ejemplo anterior (Botón Execute) para dividirlo en funciones/procedimientos </li></ul>
  83. 84. Depurador <ul><li>Se activa en: Tools / Debugger / Active </li></ul><ul><li>Breakpoint on Triggers detiene la ejecución en cada disparador o función. </li></ul><ul><li>Se pueden establecer puntos de ruptura para detener la ejecución (F9) </li></ul><ul><li>Código </li></ul><ul><li>Variables </li></ul><ul><li>Pila </li></ul><ul><li>Otras expresiones </li></ul><ul><li>Mensajes de salida </li></ul>
  84. 85. Seguimiento de código <ul><li>Se activa en : Tools / Debugger / Code Coverage </li></ul>Seguimiento del código ejecutado entre Start y Stop En Negro las sentencias ejecutadas, en Rojo las no ejecutadas
  85. 86. Asistente para código <ul><li>Se activa en: View / C/AL Symbol Menu (F5) </li></ul><ul><li>Muy útil para no cometer errores sintácticos </li></ul><ul><li>Nos muestra las variables y sus campos, además de las sentencias y escribe el código por nosotros </li></ul>
  86. 87. BLOB <ul><li>Tipo de datos de Microsoft Business Solutions Navision </li></ul><ul><ul><li>Capaz de contener gran cantidad de información (ej: imágenes) </li></ul></ul><ul><ul><li>Definir una variable de tipo BLOB y Subtipo Bitmap </li></ul></ul>
  87. 88. BLOB (y II) <ul><ul><li>Control: Picture Box </li></ul></ul><ul><ul><li>Source Expression: Producto.Imagen </li></ul></ul>
  88. 89. Formateo estándar <ul><li>Se recomiendan una serie de reglas al escribir código de forma que se integre en el existente y se pueda leer con facilidad. </li></ul><ul><ul><li>Separar con un espacio ambos lados de un operador binario (+ / := etc) </li></ul></ul><ul><li>BIEN y := (a + b) / 100; </li></ul><ul><li>MAL y := (a+b)/100; </li></ul><ul><ul><li>Indentar siempre con dos espacios. </li></ul></ul><ul><li>BIEN IF a <> '' THEN </li></ul><ul><li> Record.TESTFIELD(b); </li></ul><ul><ul><li>Usar paréntesis sí y sólo si hay que modificar la precedencia de operadores. </li></ul></ul><ul><li>BIEN IF Registrado AND (&quot;No.&quot; <> '') THEN </li></ul><ul><li> x := a + b </li></ul><ul><li>ELSE </li></ul><ul><li> x := b / (a + b); </li></ul><ul><ul><li>MAL IF (a > b) THEN </li></ul></ul><ul><li> a := 0; </li></ul>
  89. 90. Formateo estándar (II) <ul><ul><li>No usar paréntesis en llamadas a funciones sin parámetros. </li></ul></ul><ul><li>BIEN RegistrarLínea; </li></ul><ul><li>MAL RegistrarLínea(); </li></ul><ul><ul><li>IF y THEN van en la misma línea. ELSE va en línea separada. </li></ul></ul><ul><li>BIEN IF x = y THEN </li></ul><ul><li> x := x + 1 </li></ul><ul><li>ELSE </li></ul><ul><li> x := -x - 1; </li></ul>
  90. 91. Formateo estándar (III) <ul><ul><li>Las clásulas que incluyen un EXIT no deben tener parte ELSE . </li></ul></ul><ul><li>BIEN IF x <> y THEN </li></ul><ul><li> EXIT(FALSE); </li></ul><ul><li>x := x * 2; </li></ul><ul><li>MAL IF x <> y THEN </li></ul><ul><li> EXIT(FALSE) </li></ul><ul><li>ELSE </li></ul><ul><li> x := x * 2; </li></ul><ul><ul><li>Cuando BEGIN sigue a THEN o ELSE , va en su misma línea </li></ul></ul><ul><li>BIEN IF x = y THEN BEGIN </li></ul><ul><li> x := x * 2; </li></ul><ul><li> a := a - 3; </li></ul><ul><li>END; </li></ul><ul><li>MAL IF x = y THEN </li></ul><ul><li>BEGIN </li></ul><ul><li> x := x * 2; </li></ul><ul><li> a := a - 3; </li></ul><ul><li>END; </li></ul>
  91. 92. Formateo estándar (y IV) <ul><ul><li>Expresar las opciones de campos o variables de forma explícita. </li></ul></ul><ul><li>BIEN IF Tipo = Tipo::Oferta THEN </li></ul><ul><li>MAL IF Tipo = 0 THEN </li></ul><ul><ul><li>REPEAT debe ir solo en su línea y UNTIL debe ir en una línea acompañado sólo de la condición de fin. </li></ul></ul><ul><li>BIEN IF x < y THEN BEGIN </li></ul><ul><li> REPEAT </li></ul><ul><li> x := x + 1; </li></ul><ul><li> UNTIL x = y; </li></ul><ul><li> b := x; </li></ul><ul><li>END; </li></ul><ul><ul><li>WHILE y DO van en la misma línea. </li></ul></ul><ul><li> BIEN WHILE z < a DO BEGIN </li></ul><ul><li> a := a + 1; </li></ul><ul><li> b := b - 1; </li></ul><ul><li>END; </li></ul>

×