jueves, 26 de noviembre de 2015

Consumir Web Service / Servicio Web SOAP II (RTC)


Hace un tiempo publicamos un post indicando como consumir un web service desde navision(Consumir Web service SOAP ), está funcionalidad se podía utilzar en el cliente clásico y hasta la version 2009. Ahora con el cambio de platafarma y hasta la version 2016 en la que se implementa una funcionalidad específica para realizar esto, la solución que hemos encontrado es la siguiente.

En resumen, lo que se hace es crear el fichero Xml directamente luego lo enviamos al web service y leemos la respuesta. En este caso enviamos dos parámetros y recogemos un valor booleano que nos devuelve una codeunit.


Variables.

Name                  DataType      Length
URL                    Text              250
Metodo               Text              1024
NameSpace         Text              80
NodeList             Automation    'Microsoft XML, v6.0'.IXMLDOMNodeList  
xmlhttp                Automation    'Microsoft XML, v6.0'.XMLHTTP  
xmldoc                Automation    'Microsoft XML, v6.0'.DOMDocument  
soapEnvelope      Automation    'Microsoft XML, v6.0'.IXMLDOMElement  
soapBody            Automation    'Microsoft XML, v6.0'.IXMLDOMElement  
soapMethod        Automation    'Microsoft XML, v6.0'.IXMLDOMElement  
soapAtributo        Automation    'Microsoft XML, v6.0'.IXMLDOMElement  
node                    Automation    'Microsoft XML, v6.0'.IXMLDOMNode  
parametersXmlDoc    Automation    'Microsoft XML, v6.0'.DOMDocument  
xmldoc2               Automation    'Microsoft XML, v6.0'.DOMDocument   


Función LlamarWS(Producto : Code[20];TipoMod : Code[20]) Result : Boolean

Result := FALSE;

//iniciar llamada al web service
URL:='http://localhost:7047/DynamicsNAV/ws/company/Codeunit/nombreCodeunit';
Metodo:='PruebasWS';
NameSpace:='urn:microsoft-dynamics-schemas/codeunit/nombreCodeunit';

// Crear el documento xml
CREATE(xmldoc,TRUE,TRUE);

// Crear el envelope SOAP
soapEnvelope := xmldoc.createElement('Soap:Envelope');
soapEnvelope.setAttribute('xmlns:Soap', 'http://schemas.xmlsoap.org/soap/envelope/');
xmldoc.appendChild(soapEnvelope);

// Crear el cuerpo del SOAP
soapBody := xmldoc.createElement('Soap:Body');
soapEnvelope.appendChild(soapBody);

// Crear el método del body
soapMethod := xmldoc.createElement(Metodo);
soapMethod.setAttribute('xmlns', NameSpace);
soapBody.appendChild(soapMethod);

// Crear los parametros del metodo.
soapAtributo:= xmldoc.createElement('oarametro1');
soapAtributo.text(textoParametro1);
soapMethod.appendChild(soapAtributo);

soapAtributo:= xmldoc.createElement('parametro2');
soapAtributo.text(textoParametro1);
soapMethod.appendChild(soapAtributo);

//Enviar fichero al web service
CREATE(xmlhttp, TRUE, TRUE);
xmlhttp.open('POST', URL, FALSE);
xmlhttp.setRequestHeader('Content-type', 'text/xml; charset=utf-8');
xmlhttp.setRequestHeader('SOAPAction', Metodo);
xmlhttp.send(xmldoc);

//si la respeusta es Ok devuelve valor 200
IF xmlhttp.status=200 THEN
BEGIN
  xmldoc := xmlhttp.responseXML;
  xmldoc.setProperty('SelectionLanguage','XPath');
  xmldoc.setProperty('SelectionNamespaces','xmlns:tns="'+NameSpace+'"');
  NodeList := xmldoc.selectNodes('//tns:'+'return_value');
  Result := TRUE;
  if NodeList.item(0).text = 'true' then
    Exit(TRUE);
End;
Exit(FALSE);


Tambien se podria realizar la lectura del Xml devuelto pero eso ya depende de los gustos de cada uno.

Variables
xmldoc2                Automation    'Microsoft XML, v6.0'.DOMDocument   

IF ISCLEAR(xmldoc2) THEN
  CREATE(xmldoc2,TRUE,TRUE);
xmldoc2.load(xmlhttp.responseXML);
xmldoc2.save('ruta\fichero.xml');

miércoles, 22 de abril de 2015

Envío email a traves de librerias outlook


Con esta función se consigue enviar un correo utilizando las librerias de outlook. existen varias opciones tanto de formato como de tipo de envio ya sea enviado directamente o levantando el cliente Outlook para comprobar antes de enviar. Para ello primero vamos a crear dos variables locales tipo automation asignando librerias de Outlook.

    - OMail Automation 'Microsoft Outlook 9.0 Object ibrary'.MailItem
    - OApp Automation 'Microsoft Outlook 9.0 Object ibrary'.Application



En este caso se ha puesto la versión 9.0 porque es la versión que tengo instalada en mi equipo. Es necesario que todos los equipos desde donde se va lanzar esta función tengan instalada mínimo la misma versión con la que se han definido las variables. No habría problema con los equipos que tengan instalada una versión superior de las librerías ya que leerían las dll perfectamente no asi los equipos con versiones anteriores.


la funcion creada tiene varios parámetros :
eMail - dirección de correo del o de los destinatarios.
RutaPlantilla - Es la ruta al fichero de plantilla con la que abrir el correo.
RutaYFichero - Ruta del documento adjunto.
MostrarCorreo - Es un booleano para determinar si queremos o no levantar el cliente Outlook antes de enviar el correo

IF ISCLEAR(OApp) THEN
  CREATE(OApp,FALSE,TRUE);
//OMail := OApp.CreateItem(0); //Si NO utilizamos plantillas
OMail := OApp.CreateItemFromTemplate(rutaplantilla); //Si se usan plantillas
OMail."To":=eMail;
IF RutayFichero <> '' THEN
  OMail.Attachments.Add(RutayFichero); //Si queremos adjuntar un fichero
OMail.Subject :=Asunto;
//OMail.Body := ‘El cuerpo del mensaje’; //O ponemos este código o el de la plantilla.
//OMail.OriginatorDeliveryReportRequested := TRUE; //Confirmación de recepción.
//OMail.ReadReceiptRequested := TRUE; //Confirmación de lectura.
IF MostrarCorreo=TRUE THEN
  OMail.Display //levanta el cliente correo si no se llama a esta funcion envai el correo                 directamente.
ELSE
OMail.Send;



jueves, 5 de marzo de 2015

Crear Trigger en SQL para insertar un registro en el que algunos campos son de tipo "no null"

Tenemos un proceso para alimentar una tabla de navision directamente desde sql. El proceso unicamente alimenta dos campos que son el campo1 y el campo4 y quieren que se calculen el campo2 y campo3 qeu corresponden a la fecha y hora. la tabla también tiene los 4 campos definidos como no Null por lo que necesitamos que esos campos se cumplimente al insertar sin dar error ya que como hemos mencionado antes dos de los campos los tenemos que calcular, la cuestión es ¿cuando?.

Lo que hemos hecho ha sido utilizar el desencadenante INSTEAD OF INSERT, el desencadenador INSTEAD OF INSERT de una vista o tabla genera una instrucción INSERT en la tabla base mediante los datos de la tabla inserted asi tenemos acceso a los campos del registro que se está insertando, al realizar el insert sobre la tabla real dentro del trigger disponemos (consulta select de la tabla inserte) el valor de los campos insertados los dos que no venian informados los hemos calculado.

Para calcular el time lo que ponemos es la parte de fecha como a 0 pero en sql no puede haber fecha 0 y lo que hacemos es coger la primera fecha disponible "1754-01-01" y cumplimentamos la parte de la hora.  
"CAST('1754-01-01 '+CONVERT(CHAR(8), GETDATE(), 108) AS DATETIME)"

Para calcular la fecha lo que hacemos es poner la fecha de hoy y completamos a 0 la hora.
"CONVERT(CHAR(10),GETDATE(), 120)"

Definición de la tabla TablaEjemplo
Campo1 nvarchar(2)
Campo2 Fecha datetime
Campo3 Time datetime
Campo4 int

ALTER TRIGGER [dbo].[ActualizarFechas]
ON [dbo].[TablaEjemplo]
       INSTEAD OF INSERT
          AS BEGIN
            declare @cpo1 nvarchar(2); declare @cpo2 int;

            SELECT @cpo1= CampoTabla1,@cpo2=CampoTabla4
            FROM inserted
            INSERT INTO [dbo].[TablaEjemplo] ([Maquina],[Fecha],[Hora],[Contador])
            VALUES (@cpo1,
                            CONVERT(CHAR(10),GETDATE(), 120),
                            CAST('1754-01-01 '+CONVERT(CHAR(8), GETDATE(), 108) AS DATETIME),
                           @cpo2)
END

miércoles, 25 de febrero de 2015

Identificar entorno NAV versiones RTC

Exite la posibilidad en RTC de identificar la base de datos en la que nos encontramos, esto permite que en un vistazo veamos la base de datos en la que estamos trabajando ya sea en pruebas o en real en vez de tener que ir a buscar esta información cada vez que queramos comprobar donde estamos. para configurarlo es necesario ir a Información de empresa --> Indicador del sistema
En el indicador del sistema se establece el tipo de indicador, si queremos poner un texto nuestro utilzamos la opción de "texto personalizado". En el campo de enfasis se definen los matices con los que queremos que se refleje el indicador. En texto personalizado definimos el texto del indicador.

Error al exportar objetos "El archivo de objetos no se puede utilizar con esta versión del programa"

En ocasiones al realizar la exportación de un fob a una carpeta surge este error:
La solución más fácil que hemos encontrado es crear una carpeta nueva para que se pueda exportar el fichero o bien borrar los fobs de la versión superior que estén en esa ruta. Lo que hace Navision es realizar una comprobación para ver si un fichero que se ha generado en versiones de la base de datos superiores se ha exportado en esa misma ruta y al ver que si se ha realizado da el error.

viernes, 4 de noviembre de 2011

Comprobar numeración de tarjeta de crédito

Al igual que con los números de cuenta cada tipo de tarjeta de crédito tiene una configuración determinada, os voy a dejar un bloque de código donde se comprueban los formatos para los diferentes tipos de tarjetas de credito, Visa, MasteCard, American Express, Discover y Dinners Club. A ver si os vale


  vDigito:=COPYSTR(NumeroTarjeta,1,1);
  vDigito2:=COPYSTR(NumeroTarjeta,1,2);
  vDigito4:=COPYSTR(NumeroTarjeta,1,4);

//American express
  IF "Tipo Tarjeta"="Tipo Tarjeta"::"American Express" THEN BEGIN
    IF (vDigito2 = '34') OR (vDigito2 = '37') THEN BEGIN
      IF (STRLEN(NumeroTarjeta) <> 15) THEN
        ERROR(TXT1,STRLEN(NumeroTarjeta),"Tipo Tarjeta");
    END
    ELSE
      ERROR(TXT2,rPolizas.NumeroTarjeta,rPolizas."Tipo Tarjeta");
  END;

//Visa
  IF "Tipo Tarjeta"="Tipo Tarjeta"::Visa THEN BEGIN
    IF vDigito = '4' THEN BEGIN
      IF (STRLEN(NumeroTarjeta) <> 13) AND (STRLEN(NumeroTarjeta) <> 16) THEN
        ERROR(TXT1,STRLEN(NumeroTarjeta),"Tipo Tarjeta");
    END
    ELSE
      ERROR(TXT2,NumeroTarjeta,"Tipo Tarjeta");
  END;

//MasterCard
  IF "Tipo Tarjeta"="Tipo Tarjeta"::MasterCard THEN BEGIN
    IF (vDigito2 >= '51') AND (vDigito2 <= '55') THEN BEGIN
      IF (STRLEN(NumeroTarjeta) <> 16) THEN
        ERROR(TXT1,STRLEN(NumeroTarjeta),"Tipo Tarjeta");
    END
    ELSE
         ERROR(TXT2,NumeroTarjeta,"Tipo Tarjeta");
  END;

//Discover
  IF "Tipo Tarjeta"=."Tipo Tarjeta"::Discover THEN BEGIN
    IF vDigito4 = '6011' THEN BEGIN
      IF (STRLEN(NumeroTarjeta) <> 16) THEN
        ERROR(TXT1,STRLEN(NumeroTarjeta),"Tipo Tarjeta");
    END
    ELSE
      ERROR(TXT2,NumeroTarjeta,"Tipo Tarjeta");
  END;  

//Diners club
  IF "Tipo Tarjeta"="Tipo Tarjeta"::"Diners club" THEN BEGIN
    IF (vDigito2 = '30') OR (vDigito2 = '36') OR (vDigito2 = '38') THEN BEGIN
      IF (STRLEN(NumeroTarjeta) <> 14) THEN
        ERROR(TXT1,STRLEN(NumeroTarjeta),"Tipo Tarjeta");
    END
    ELSE
      ERROR(TXT2,NumeroTarjeta,"Tipo Tarjeta");
  END;

//Comprobar numeración de tarjeta
  vNuevoNT:='';
  FOR vIndex:=1 TO STRLEN(NumeroTarjeta) DO BEGIN
     vNuevoNT:='';
     IF vIndex MOD 2 <> 0 THEN BEGIN
        EVALUATE(vNoperar,FORMAT(NumeroTarjeta[vIndex]));
        IF vNoperar * 2 > 9 THEN BEGIN
          vNuevoNT:=FORMAT((vNoperar*2)-9);
        END
        ELSE BEGIN
            vNuevoNT:=FORMAT((vNoperar*2));
        END    
     END
     ELSE BEGIN
         vNuevoNT:=FORMAT(NumeroTarjeta[vIndex]);
     END;
     EVALUATE(vConvert,vNuevoNT);
     vResultado+=vConvert;
  END;
  IF (vResultado MOD 10 <> 0) AND (vResultado > 150) THEN
    ERROR('El numero de tarjeta %1 no es correcto',NumeroTarjeta);
END;

Sacar el dígito control de numero cuenta para comprobar si es correcto

Voy a dejar un bloque de código donde se realiza la comprobación del dígito de control de un número de cuenta para saber si este es correcto.



//Comprobar longitud y numero de cuenta
Suma1Int:=0;
Suma2Int:=0;
IF vTXTNumCuenta <> '' THEN BEGIN
  FOR vIndex:=1 TO 10 DO BEGIN
     Suma1:=0;
     Suma2:=0;
     EVALUATE(Digit1,COPYSTR(vTXTNumCuenta,9,1));
     EVALUATE(Digit2,COPYSTR(vTXTNumCuenta,10,1));
     CASE vIndex  OF
       1: BEGIN
            EVALUATE(Suma1,COPYSTR(vTXTNumCuenta,1,1));
            EVALUATE(Suma2,COPYSTR(vTXTNumCuenta,11,1));
            Suma1Int:=Suma1*4;
            Suma2Int:=Suma2*1;
          END;

       2: BEGIN
            EVALUATE(Suma1,COPYSTR(vTXTNumCuenta,2,1));
            EVALUATE(Suma2,COPYSTR(vTXTNumCuenta,12,1));
            Suma1Int+=Suma1*8;
            Suma2Int+=Suma2*2;
          END;

       3: BEGIN
            EVALUATE(Suma1,COPYSTR(vTXTNumCuenta,3,1));
            EVALUATE(Suma2,COPYSTR(vTXTNumCuenta,13,1));
            Suma1Int+=Suma1*5;
            Suma2Int+=Suma2*4;
          END;

       4: BEGIN
            EVALUATE(Suma1,COPYSTR(vTXTNumCuenta,4,1));
            EVALUATE(Suma2,COPYSTR(vTXTNumCuenta,14,1));
            Suma1Int+=Suma1*10;
            Suma2Int+=Suma2*8;
          END;

       5: BEGIN
            EVALUATE(Suma1,COPYSTR(vTXTNumCuenta,5,1));
            EVALUATE(Suma2,COPYSTR(vTXTNumCuenta,15,1));
            Suma1Int+=Suma1*9;
            Suma2Int+=Suma2*5;
          END;

       6: BEGIN
            EVALUATE(Suma1,COPYSTR(vTXTNumCuenta,6,1));
            EVALUATE(Suma2,COPYSTR(vTXTNumCuenta,16,1));
            Suma1Int+=Suma1*7;
            Suma2Int+=Suma2*10;
          END;

       7: BEGIN
            EVALUATE(Suma1,COPYSTR(vTXTNumCuenta,7,1));
            EVALUATE(Suma2,COPYSTR(vTXTNumCuenta,17,1));
            Suma1Int+=Suma1*3;
            Suma2Int+=Suma2*9;
          END;

       8: BEGIN
            EVALUATE(Suma1,COPYSTR(vTXTNumCuenta,8,1));
            EVALUATE(Suma2,COPYSTR(vTXTNumCuenta,18,1));
            Suma1Int+=Suma1*6;
            Suma2Int+=Suma2*7;
          END;

       9: BEGIN
            EVALUATE(Suma2,COPYSTR(vTXTNumCuenta,19,1));
            Suma2Int+=Suma2*3;
          END;

       10:  BEGIN
              EVALUATE(Suma2,COPYSTR(vTXTNumCuenta,20,1));
              Suma2Int+=Suma2*6;
            END;
     END;
  END;
//Primer dígito de control entidad+sucursal
  Parc1:=0;
  Parc1:= 11 - (Suma1Int MOD 11);
  IF Parc1 > 9 THEN
    Parc1:=11 - Parc1;

//Segundo dígito de control Numero Cuenta
  Parc2:=0;
  Parc2:=11 - (Suma2Int MOD 11);
  IF Parc2 > 9 THEN
    Parc2:=11 - Parc2;

  IF (Digit1 = Parc1) AND (Digit2 = Parc2) THEN BEGIN
  END
  ELSE BEGIN
    ERROR('Número cuenta erroneo, no corresponde el DC: %1%2',Parc1,Parc2);
  END;
END;