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