Yo y Nexus: la consumación

jilguero«Maestro, maestro, contemplad a los bellos jilgueros como danzan en el ritual de su amor en este soleado y florido día». «Así es la naturaleza, incrédulo novicio» dijo el maestro. Tras una breve pausa, embelesado por la escena continuó diciendo: «Cuando dos seres puros alcanzan un nivel de comunicación tan fluida y franca como la caída del río tras el deshielo de primavera, solo puede devenir la belleza, la perfección de formas, el sublime ying…» <plash> sonó interrumpiéndole su meditación en voz alta. «Deja el tirachinas y céntrate en la lección» dijo, mientras suplicaba a los cielos por la llegada de su nueva escupidera de bronce…

Para terminar esta serie de artículos de introducción a la ampliación de funcionalidades de Nexus mediante lo que denominamos «DLL de terceros» (ya sabemos que puede ser una DLL o un objeto COM), vamos a comentar la información que nos transmite Nexus mediante los eventos que indicamos en el primer artículo de la serie.

Comentaremos los parámetros a partir de la signatura o definición de las mismas en Delphi. El significado (y casi todos los tipos) son iguales para las definiciones en C#. Solo hay una sutil diferencia para los casos en los que se envíen un identificador, un registro completo o conjunto de datos. Pero lo comentaremos cuando lleguemos a ese punto.

En general, los parámetros de las funciones con las que nos subscribimos a los eventos de Nexus son simples valores escalares, como:

Tipo Delphi C# (.Net) VB6
Cadena de caracteres string string (System.String) String
Reales double float (System.Double) Double
Enteros integer int (System.Int32) Integer
Booleano (Verdadero/Falso) boolean bool (System.Boolean) Boolean

Aparte de estos tipos escalares, nos encontramos con tres tipos complejos (matrices) que son «Identificador», «Registro» y «Conjunto de datos». Los tres nos llegan como matrices, en un caso unidimensional y en dos de ellos multidimensionales:

Tipo Delphi C# (.NET) VB6
Matriz Variant object[] Variant() (OleVariant)

El contenido de estas matrices es el siguiente:

Tipo Contenido
Identificador

Es una matriz unidimensional de cadenas, en la que hay tantos elementos como campos conformen la llave primaria del elemento del que se trate (Se usa para maestros, Ver Diccionario.EXE). Así, por ejemplo, el maestro que estamos escuchando tiene una clave compuesta de varios campos, nos llegará una matriz con tantos elementos como campos compongan la llave primaria y en el orden en el que están definidos en el diccionario.

Array[0]…Array[n-1] con n = número de campos llave primaria.

Registro

Se trata de una matriz multidimensional en la que el primer elemento, que existe siempre, indica el número entero de elementos (campos) que componen la matriz. Estos datos son, a su vez, matrices unidimensionales de dos elementos cadena, en las que primero tenemos el nombre del campo y en el segundo el valor de dicho campo.

(Nota: en versión 9.6.10 se ha cambiado el tipo del valor. Más información aquí)

Matriz:

Array[0] : Número de Campos (desde índice 1 a n).

Array[1]: Campo 1.

Array[n]: Campo N-ésimo.

Campo:

Campo[0]: (string) Nombre del campo.

Campo[1]: (string) Valor del campo.

Para acceder al nombre del quinto campo del registro  tendremos que hacer (Delphi):

MiValor := MiConjuntoDeDatos[5][0]

donde:

5 = Quinto campo.

0=Nombre del campo.

Conjunto de datos

Es una matriz  multidimensional en la que el primer elemento indica el número entero de «Registros» que lo compone. A partir de éste, cada elemento es un Registro.

Matriz:

Array[0]: Número de registros (desde índice 1 a n).

Array[1]: Registro 1.

Array[n]:  Registro n-ésimo.

Así, si queremos acceder al valor del tercer campo del segundo registro de un conjunto de datos tendremos que hacer (Delphi):

MiValor := MiConjuntoDeDatos[2][3][1]

donde:

2 = Segundo registro.

3 = Tercer campo.

1=Valor

La sutil diferencia, que comentábamos antes entre las definiciones de Delphi (y VB6) y C# (.NET en general), es que mientras en el primero recibimos una matriz vía un Variant (u OleVariant para el caso de que implementemos un objeto COM), en .NET recibimos una matriz de  System.Object (System.Array). Aquí es más facil (en .NET) conocer el número de elementos ya que la matriz de objetos ya tiene una propiedad «Length» que nos lo devuelve. Eso sí, como hay un primer elemento (salvo en el caso de Identificador) con el número de elementos que le sigue, el valor de «Length» para la matriz de objetos será, en realidad, el número de elementos más uno (ése que acabamos de comentar). De todas formas es recomendable comprobar el valor del primer elemento. En el caso de un tipo «Identificador«, «Length» sí nos devolverá el número de valores.

Inicialización/finalización de la DLL

Los eventos que nos avisan de cuando se comienza a usar nuestra DLL son los siguientes:

  Iniciar: procedure(Empresa: PChar); stdcall;
  IniciarConSistema: procedure(Empresa, Sistema: PChar); stdcall;
  Finalizar: procedure; stdcall;

«Iniciar» o «IniciarConSistema» nos avisan de la carga de nuestra DLL, ya sea porque acabamos de entrar en Nexus en una determinada empresa o cambiemos a otra. Aquí tenemos que comentar una cosa. Sólo se llamará a una de las dos. De hecho, de existir ambas solo se llamará a «IniciarConSistema».

«Finalizar» nos notifica la descarga de la misma (igual que en el caso anterior, por el cambio a otra empresa o porque se cierre Nexus).

Empresa: Cadena de conexión a la base de datos de la empresa a la que se ha conectado el Nexus.

Sistema: Cadena de conexión a la base de datos de sistema que está usando el Nexus.

Nota: A día de hoy, la cadena de conexión es ADO.

Documentos

  DespuesDeGuardarDocumento: procedure(Documento: string; IdDoc: Double); stdcall;
  DespuesDeGuardarDocumentoV2: procedure(Documento: String; IdDoc: Double; Estado: Integer); stdcall;
  AntesDeGuardarDocumento: function(Documento: string; IdDoc: Double; var Cabecera: Variant; var Lineas: Variant): boolean; stdcall;
  AntesDeGuardarDocumentoV2: function(Documento: string; IdDoc: Double; var Cabecera: Variant; var Lineas: Variant; Estado:Integer): boolean; stdcall;
  AntesDeGuardarLinea: function(Documento: String; Cabecera: Variant; Linea: Variant): Variant; stdcall;
  AntesDeGuardarLineaConDetalle: function(Documento: String; Cabecera: Variant; Linea: Variant;Detalle: Variant): Variant; stdcall;
  DespuesDeGuardarLinea: procedure(Documento: String; Cabecera: Variant; Linea: Variant); stdcall;
  // Futurible: (hoy por hoy no se ejecuta).
  DespuesDeGuardarLineaConDetalle: procedure(Documento: String; Cabecera: Variant; Linea: Variant; Detalle: Variant); stdcall;

Las cabeceras de las funciones ya son suficientemente explicativas. Aquí nos encontramos los eventos que se producen AntesDe guardar un documento, o una de sus líneas, y DespuesDe (si no se ha producido algún error u algún AntesDe abortó el proceso) guardarlo.

Las funciones que devuelvan un valor booleano, indican si se debe continuar el guardado o por el contrario se debe abortar. En el caso de AntesDeGuardarLineaXXX, se devuelve un Variant. En este valor, si el variant es nulo (varNull), la ejecución continuará, si es la cadena ‘F’, se aborta el proceso (estos dos casos son equivalentes al caso de las funciones que devuelven un valor booleano, con los valores True y False respectivamente). Ahora, si el valor es un Registro, indicamos que debe continuar cambiando los valores de los campos indicado en el mismo.

Nota: Se recomienda el uso de las funciones DespuesDeGuardarDocumentoV2 y AntesDeGuardarDocumentoV2 sobreDespuesDeGuardarDocumento y AntesDeGuardarDocumento respectivamente. La información de estado pueder muy útil.

Nota 2: Que un parámetro sea «var» (ref de c#) no quiere decir que se pueda usar como parámetro de salida. De hecho, AntesDeGuardarDocumento y AntesDeGuardarDocumentoV2 solo permiten cambios en el parámetro cabecera a partir de la versión 8 del producto NEXUS/a3ERP como se puede leer aquí.

Los Eventos AntesDe pueden abortar el proceso de guardado si devuelven FALSE (True para que el guardado continúe).

Documento: Tipo del documento desde el cual se genera el evento.

Valor Significado
OC Oferta de compra
OV Oferta de venta
PC Pedido de compra
PV Pedido de venta
AC Albarán de compra
AV Albarán de venta
DC Deposito de compra
DV Deposito de venta
FC Factura de compra
FV Factura de venta
TR Traspaso
IN Inventario
PR Producción
AP Apunte
RE Regularización

IdDoc: Identificador interno del documento (PK de la tabla de cabecera del documento).

Estado: Tipo de modificación del registro:

Valor Significado
0 Alta
1 Modificación
2 Baja

Cabecera:

En documentos: [Conjunto de datos] de campos del documento.

Para los eventos de línea [Registro] de campos de la cabecera del documento

Líneas: [Conjunto de datos] de líneas del documento.

Línea: [Registro] de campos de la línea del documento.

Detalle: [Conjunto de datos] de detalle de la línea del documento.

Cartera

  AntesDeGuardarEfecto: function( Operacion: string; Datos: Variant): boolean; stdcall;
  DespuesDeGuardarEfecto: procedure( Operacion: string; NumCartera: Double; NumVen: integer); stdcall;
  AntesDeGuardarRemesa: function( Operacion, Tipo: string; Cabecera, Lineas: variant):boolean; stdcall;
  DespuesDeGuardarRemesa: procedure( Operacion, Tipo: string; IdRemesa: Double); stdcall;

Al igual que en el bloque de funciones anterior, aquí los eventos se producen AntesDe o DespuesDe guardar alguno de los elementos de Cartera de Nexus.

Los Eventos AntesDe pueden abortar el proceso de guardado si devuelven FALSE (True para que el guardado continúe).

Operación

Valor Significado
PA Pago
APA Anulación de pago
CO Cobro
COD Cobro devuelto
ACO Anulación de cobro
BOR Borrado
MOD Modificación

Tipo

Valor Significado
C Cobro
P Pago

NumCartera: Número de cartera.

NumVen: Número de vencimiento.

IdRemesa: Identificado interno de la remesa (PK de la tabla de remesas).

Datos: [Registro] de datos del efecto.

Cabecera: [Registro] de campos de la remesa.

Líneas: [Conjunto de datos] de líneas de la remesa.

Maestros

  AntesDeGuardarMaestro: function(Tabla: String; Datos: Variant): Boolean; stdcall;
  AntesDeGuardarMaestroV2: function(Tabla: String; Datos: Variant; Estado:Integer): Boolean; stdcall;
  DespuesDeGuardarMaestro: procedure(Tabla: String; Datos: Variant); stdcall;
  DespuesDeGuardarMaestroV2: procedure(Tabla: String; Datos: Variant; Estado:Integer); stdcall;
  AntesDeBorrarMaestro: function( Tabla: string; IdMaestro: Variant): boolean; stdcall;
  DespuesDeBorrarMaestro: procedure( Tabla: string; IdMaestro: Variant); stdcall;

En este caso, los eventos se producen AntesDe o DespuesDe guardar o borrar alguno de los maestros de Nexus.

Igual que en los casos anteriores, los Eventos AntesDe pueden abortar el proceso (de guardado o borrado) si devuelven FALSE (True para que el guardado continúe).

Tabla: Nombre de la tabla del maestro en base de datos.

IdMaestro: [Identificador] interno del maestro.

Datos: [Registro] de campos del maestro.

Apuntes

  AntesDeGuardarApunte: function( Apunte: variant):boolean; stdcall;
  AntesDeGuardarAsiento: function( IdAsiento: Double; Asiento: variant):boolean; stdcall;
  DespuesDeGuardarAsiento: procedure( IdAsiento: Double; Asiento: variant); stdcall;

IdAsiento: Identificador interno del asiento (PK en la tabla de asientos).

Apunte: [Registro] de campos del apunte (línea de asiento).

Asiento: [Conjunto de datos] con las líneas de apunte que lo forman.

Y con esto y un bizcocho no caducado (el cual estáis obligados a mandar debido a que este texto es un EULA entre usted y yo, y que usted acepta al haber leído la primera palabra del presente párrafo) termino esta serie de artículos de introducción a la programación de dlls de terceros en Nexus.

Agradecimientos: Javier López aka «La solución».

Agradecimientos: Daniel «tiskimikis» S.D.L.M antes de SIE ahora de WKE, por 2 veces ya.

Agradecimientos: Didac «Gambler» Punyet.

– «Maestro, maestro, las hojas caen de los árboles. ¿Qué significa esto?» – «El otoño llega (en el corte inglés) como preludio del invierno de nuestras vidas» «¿Ein?» – «Que se terminó, finito, kaput, que te vayas a casa, que me dejes en paz, adiós, good bye, sayonara, make my day…».

¿Se perdió los anteriores y apasionantes episodios de esta entrega? No contaremos el número de emmies, oscars y goyas porque somos modestos… Visite las reposiciones en nuestro canal de TDT :  Cuando Nexus habla, Fusionándose con Nexus, y Dll y Nexus, el encuentro.

Puede encontrar otros artículos que actualizan información de esta serie aquí: Eventos Nexus: V7 versus V8 y Nexus vs Eventos. The Final Chapter.

Acerca de El monstruo de Caerbannog

Temible guardián de la gruta que esconde un temible y obscuro secreto...

Publicado el marzo 26, 2009 en Desarrolladores, Distribuidores, Implantaciones y etiquetado en , , , , . Guarda el enlace permanente. 2 comentarios.

  1. Hola, haciendo pruebas con AntesDeGuardarEfecto, me funciona (casi) correctamente.

    Creo una factura nueva con repercusiones a cartera, me salta el evento y en el argumento «Datos» me vienen todos los datos del efecto que va a crear.

    El PERO es que si la forma de pago elegida tiene 2 vencimientos (30,60), solo me muestra los «Datos» del 2º vencimiento.

    Osea, en(.NET)

    Cuando Datos(i)(0) es «FECHAVALOR»:
    Datos(i)(1) es la fecha del 2º vencimiento, nunca del 1º.

    Como puedo acceder a los diferentes vencimientos?

    Puedo arreglarlo con DespuesDeGuardarEfecto, pero …

    Muchas gracias y enhorabuena por el BLOG (imprescindible)

    Un saludo,

  2. Este es el contestador automático del Monstruo de Caerbannog:
    Hola David, en este momento me encuentro hibernando, como casi todo el año. Solo respondo cuando cierta persona me pincha con un palo. Por eso le recomiendo, y a cualquier otra persona que tenga algún problema con sus programaciones a medida, que se ponga en contacto con el departamento de atención al cliente.

    Pero puedo indicarte que debe tratarse de un error. Hable con el departamento de atención al cliente y añada que seguramente estén pasando solo el vencimiento actual en vez de todos los vencimientos a su función. No olvide que no debe mencionar esta conversación. Es posible que le pongan en la lista negra si llegan a saber de ella.

Deja un comentario