My Soap Service Proxy Codeunit

Up to now we in Advania have been using the method described here on my blog to connect to most of the Soap web services that we needed to integrate with.

The problem with this method is that we have to manage a lot of DLLs.  This has caused some issues and problems.

Another thing is that we are moving to AL.  And in AL we can’t just throw in a custom DLL to do all the work.

In C/AL We can do this with standard dotnet objects

        DOMDoc := DOMDoc.XmlDocument;
        DOMProcessingInstruction := DOMDoc.CreateProcessingInstruction('xml','version="1.0" encoding="utf-8"');
        DOMDoc.AppendChild(DOMProcessingInstruction);
        DOMElement := DOMDoc.CreateElement('soap:Envelope','http://schemas.xmlsoap.org/soap/envelope/');
        DOMElement.SetAttribute('xmlns:soap','http://schemas.xmlsoap.org/soap/envelope/');
        DOMElement.SetAttribute('xmlns:xsi','http://www.w3.org/2001/XMLSchema-instance');
        DOMElement.SetAttribute('xmlns:xsd','http://www.w3.org/2001/XMLSchema');
        DOMElement.SetAttribute('xmlns:ws','http://ws.msggw.siminn');

        DOMElement2 := DOMDoc.CreateElement('soap:Header','http://schemas.xmlsoap.org/soap/envelope/');
        DOMElement.AppendChild(DOMElement2);

        DOMElement2 := DOMDoc.CreateElement('soap:Body','http://schemas.xmlsoap.org/soap/envelope/');
        DOMElement3 := DOMDoc.CreateElement('ws:sendSMS','http://ws.msggw.siminn');
        DOMElement4 := DOMDoc.CreateElement('ws:username','http://ws.msggw.siminn');
        DOMElement4.InnerText := SMSSetup."Service User Name";
        DOMElement3.AppendChild(DOMElement4);
        DOMElement4 := DOMDoc.CreateElement('ws:password','http://ws.msggw.siminn');
        DOMElement4.InnerText := SMSSetup."Service Password";
        DOMElement3.AppendChild(DOMElement4);
        DOMElement4 := DOMDoc.CreateElement('ws:source','http://ws.msggw.siminn');
        DOMElement4.InnerText := SMSSetup.Sender;
        DOMElement3.AppendChild(DOMElement4);
        DOMElement4 := DOMDoc.CreateElement('ws:destination','http://ws.msggw.siminn');
        DOMElement4.InnerText := SendTo;
        DOMElement3.AppendChild(DOMElement4);
        DOMElement4 := DOMDoc.CreateElement('ws:text','http://ws.msggw.siminn');
        DOMElement4.InnerText := SendText;
        DOMElement3.AppendChild(DOMElement4);
        DOMElement4 := DOMDoc.CreateElement('ws:encoding','http://ws.msggw.siminn');
        DOMElement4.InnerText := '0';
        DOMElement3.AppendChild(DOMElement4);
        DOMElement4 := DOMDoc.CreateElement('ws:flash','http://ws.msggw.siminn');
        DOMElement4.InnerText := '0';
        DOMElement3.AppendChild(DOMElement4);
        DOMElement2.AppendChild(DOMElement3);
        DOMElement.AppendChild(DOMElement2);
        DOMDoc.AppendChild(DOMElement);

        HttpWebRequest := HttpWebRequest.Create(SMSSetup."SOAP URL");
        HttpWebRequest.Timeout := 30000;
        HttpWebRequest.UseDefaultCredentials(TRUE);
        HttpWebRequest.Method := 'POST';
        HttpWebRequest.ContentType := 'text/xml; charset=utf-8';
        HttpWebRequest.Accept := 'text/xml';
        HttpWebRequest.Headers.Add('soapAction','urn:sendSMS');
        MemoryStream := HttpWebRequest.GetRequestStream;
        DOMDoc.Save(MemoryStream);
        MemoryStream.Flush;
        MemoryStream.Close;

        NAVWebRequest := NAVWebRequest.NAVWebRequest;
        IF NOT NAVWebRequest.doRequest(HttpWebRequest,HttpWebException,HttpWebResponse) THEN
          ERROR(Text003,HttpWebException.Status.ToString,HttpWebException.Message);

        MemoryStream := HttpWebResponse.GetResponseStream;
        DOMResponseDoc := DOMResponseDoc.XmlDocument;
        DOMResponseDoc.Load(MemoryStream);
        MemoryStream.Flush;
        MemoryStream.Close;

        ReceivedNameSpaceMgt := ReceivedNameSpaceMgt.XmlNamespaceManager(DOMResponseDoc.NameTable);
        ReceivedNameSpaceMgt.AddNamespace('ns','http://ws.msggw.siminn');
        DOMNode := DOMResponseDoc.SelectSingleNode('//ns:return',ReceivedNameSpaceMgt);

        Response := DOMNode.InnerText;
        Success :=  Response = 'SUCCESS';
        IF ShowResult AND Success THEN
          MESSAGE(Text001)
        ELSE IF ShowResult AND NOT Success THEN
          ERROR(Text005,Response);

AL code to do the same with the built in AL objects but that code is not much shorter.

With a custom proxy DLL the code would be

Proxy := Proxy.SMSWS;
Proxy.Url := SMSSetup."SOAP URL";
Response := Proxy.sendSMS(Username,Password,SenderText,SendTo,SendText,'0',FALSE,FALSE,'0');
Success :=  Response = 'SUCCESS';
IF ShowResult AND Success THEN
  MESSAGE(Text001)
ELSE IF ShowResult AND NOT Success THEN
  ERROR(Text005,Response);

With this example we can easily see why we have chosen to create a proxy DLL for most of the Soap services.

I wanted to find a way to make things easier in AL and I remembered having dealt with C/AL objects by Vjeko from some time ago.  I took another look and that code helped me to get started.

The result is a Soap Proxy Client Mgt. Codeunit in C/AL that I have sent to Microsoft’s cal-open-library project asking to have this code put into the standard C/AL library.

Using this Codeunit the code will be like this.

  WITH SoapProxyClientMgt DO BEGIN
    CreateSoapProxy(SMSSetup."SOAP URL");
    InitParameters(9);
    SetParameterValue(Username,1);
    SetParameterValue(Password,2);
    SetParameterValue(SenderText,3);
    SetParameterValue(SendTo,4);
    SetParameterValue(SendText,5);
    SetParameterValue('0',6);
    SetParameterValue(FALSE,7);
    SetParameterValue(FALSE,8);
    SetParameterValue('0',9);
    InvokeMethod('SMSWS','sendSMS',TempBlob);
    XmlBuffer.LoadFromText(TempBlob.ReadAsTextWithCRLFLineSeparator);
    IF XmlBuffer.FindNodesByXPath(XmlBuffer,'/string') THEN
      Response := XmlBuffer.Value;
    Success :=  Response = 'SUCCESS';
    IF ShowResult AND Success THEN
      MESSAGE(Text001)
    ELSE IF ShowResult AND NOT Success THEN
      ERROR(Text005,Response);
  END;

What about AL?

For now this C/AL Codeunit is not in the standard CRONUS database.  I need to import the C/AL code and make sure that AL will be able to use that Codeunit.  You can see how to do this in my last blog post.

This C/AL Code will directly convert to AL and is ready to use.

          with SoapProxyClientMgt do begin
            CreateSoapProxy(SMSSetup."SOAP URL");
            InitParameters(9);
            SetParameterValue(Username,1);
            SetParameterValue(Password,2);
            SetParameterValue(SenderText,3);
            SetParameterValue(SendTo,4);
            SetParameterValue(SendText,5);
            SetParameterValue('0',6);
            SetParameterValue(false,7);
            SetParameterValue(false,8);
            SetParameterValue('0',9);
            InvokeMethod('SMSWS','sendSMS',TempBlob);
            XmlBuffer.LoadFromText(TempBlob.ReadAsTextWithCRLFLineSeparator);
            if XmlBuffer.FindNodesByXPath(XmlBuffer,'/string') then                        
              Response := XmlBuffer.Value;        
            Success :=  Response = 'SUCCESS';
            if ShowResult and Success then
              MESSAGE(Text001)
            else if ShowResult and not Success then
              ERROR(Text005,Response);
          end;

More examples on how to use this Proxy Codeunit will follow.  Stay tuned…

Windows 10 and Universal Apps

A new operating system from Microsoft sure has it’s impact on our ecosystem.  Our Microsoft Dynamics NAV is based on top of Microsoft operating systems.

With the power of SOAP and OData services in Microsoft Dynamics NAV everyone is now able to design a custom user interface dedicated for a simple task, both for employees and customers.

It should be interesting to grab the Visual Studio 2015 tools and start to experiment with the Windows Universal developement with Microsoft Dynamics NAV on the back-end.

The end goal, to have Windows running on all type of devices will open a lot of possibilities.  Go wild…

Using NetTcpPortSharing for NAV Servers

I just came back from three weeks vacation yesterday.  During my vacation I had made a decision to implement Tcp Port Sharing for the Instance Administration tool used in Advania Azure.

Early last year I published a function that uses the sc.exe to modify a NAV Service startup type.  When a NAV Service is installed and configured in setup, the startup type is Automatic (Delayed Start).  However, create a new service with Powershell New-NavServerIntance and the statup type is Automatic without the (Delayed Start).

To enable Tcp Port Sharing that same sc.exe function is needed.  Interestingly, after I had finished the task and was reading NAV blogs I saw that Waldo just published a powershell function on his blog to do just this.

The script lines I used and added to my Instance Administration powershell scripts are based on my fist sc.exe function but not using the function it self.  Now when a new NAV service is created by the tool the startup type is modified and if so selected by the deployment settings, the Tcp Port Sharing is also activated.

By default, the Tcp Port Sharing service is disabled.
servicedisabled

The startup type should be changed to Manual.  This can be done manually or by an administrative powershell script.

[code lang=”powershell”]#Set Startup Mode for NetTcpPortSharing to Manual
$command = ‘sc.exe \\$Computer config "NetTcpPortSharing" start= demand’
$Output = Invoke-Expression -Command $Command -ErrorAction Stop
if($LASTEXITCODE -ne 0){
Write-Error "$Computer : Failed to set NetTcpPortSharing to manual start. More details: $Output"
}
[/code]

Similar script is used to update the existing NAV Services to both delayed start and Tcp Port Sharing dependency.

[code lang=”powershell”]

#Stop NAV Server Instances
Get-NAVServerInstance | Set-NAVServerInstance -Stop
#Update Startup Type and Dependency on NAV Server Instances
Get-NAVServerInstance | foreach {
$Service = $_.ServerInstance
Write-Host "Working on service $Service"
$Computer = ‘LOCALHOST’
$command = ‘sc.exe \\$Computer config "$Service" start= delayed-auto’
$Output = Invoke-Expression -Command $Command -ErrorAction Stop
if($LASTEXITCODE -ne 0){
Write-Error "$Computer : Failed to set $Service to delayed start. More details: $Output"
}
$command = ‘sc.exe \\$Computer config "$Service" depend= NetTcpPortSharing/HTTP’
$Output = Invoke-Expression -Command $Command -ErrorAction Stop
if($LASTEXITCODE -ne 0){
Write-Error "$Computer : Failed to set $Service TcpPortSharing. More details: $Output" -foregroundcolor red
}

}
#Start NAV Server Instances
Get-NAVServerInstance | Set-NAVServerInstance -Start
[/code]

It should be obvious that the above script can also use the Set-ServiceStartupMode from my blog and the Enable-NAVServerInstancePortSharing function on Waldo’s blog. That would be a cleaner code and more in line with what we would like to see.

Again quoting Waldo from his previous blog, “When you’re using a dedicated service account, things might become a slight more difficult”.  That is exactly my case, I am using a dedicated service account.

After enabling Tcp Port Sharing and updating the services they would not start.  Event Viewer revealed the reason.

Server instance: CRONUS
The service MicrosoftDynamicsNavServer$CRONUS failed to start. This could be caused by a configuration error. Detailed error information:System.ServiceModel.CommunicationException: The service endpoint failed to listen on the URI ‘net.tcp://mynavserver.dynamics.is:7046/CRONUS/Service’ because access was denied. Verify that the current user is granted access in the appropriate allowAccounts section of SMSvcHost.exe.config. —> System.ComponentModel.Win32Exception: Access is denied

So I started to ask Bing what I could do.  Microsoft MSDN states:

When a net.tcp binding enables port sharing (by setting portSharingEnabled =true on the transport binding element), it implicitly allows an external process (namely the SMSvcHost.exe, which hosts the Net.TCP Port Sharing Service) to manage the TCP socket on its behalf.

Hence, I need to add the Sid of my NAV Service Account to the SMSvcHost.exe.config file.  I could do this manually, but I am a programmer!

Another powershell script was born.  This one could also be converted to a function.  Before executing the script make sure to update the user and domain in the top of the script.  Be smart and execute this function before updating the NAV Services with the script above.

[code lang=”powershell”]
#Modify User and Domain to fit your environment
$UserToAdd = ‘srvNAV’
$UserDomainToAdd = ‘DYNAMICS’

#Initial Values
$UserSidFound = ‘false’
$ConfigurationSet = ‘false’

#Net.Tcp Port Sharing Service Name
$ServiceName = ‘NetTcpPortSharing’

#Get SID for the Service User
$UserSid = ([wmi] "win32_userAccount.Domain=’$UserDomainToAdd’,Name=’$UserToAdd’").SID

#Get Path for SMSvcHost.exe.config file
$SMSvcHostPath = (Get-WmiObject win32_service | ?{$_.Name -like $ServiceName} ).PathName
$SMSvcHostPathConfig = $SMSvcHostPath + ‘.config’

Write-Host "Reading XML from $SMSvcHostPathConfig"
#Read Config file
$xmlDoc = [xml] (Get-Content $SMSvcHostPathConfig)

Write-Host "Looking for access permission for $UserSid"
#Loop through allowed accounts and search for the service user Sid
$allowAccounts = Select-Xml "configuration/system.serviceModel.activation/net.tcp/allowAccounts/add" $xmlDoc
$allowAccounts | ForEach-Object {
$ConfiguredSid = $_.Node.Attributes.Item(0).Value
if ($ConfiguredSid -eq $UserSid) {$UserSidFound = ‘true’}
$ConfigurationSet = ‘true’
Write-Host "Found SID $ConfiguredSid"
}

#Act if Access Configuration is not enabled
if ($ConfigurationSet -eq ‘false’) {Write-Host "Access permission not configured"
$config = [xml] ‘<system.serviceModel.activation>
<net.tcp listenBacklog="10" maxPendingConnections="100" maxPendingAccepts="2" receiveTimeout="00:00:10" teredoEnabled="false">
<allowAccounts>
<add securityIdentifier="S-1-5-18"/>
<add securityIdentifier="S-1-5-19"/>
<add securityIdentifier="S-1-5-20"/>
<add securityIdentifier="S-1-5-32-544" />
</allowAccounts>
</net.tcp>
<net.pipe maxPendingConnections="100" maxPendingAccepts="2" receiveTimeout="00:00:10">
<allowAccounts>
<add securityIdentifier="S-1-5-18"/>
<add securityIdentifier="S-1-5-19"/>
<add securityIdentifier="S-1-5-20"/>
<add securityIdentifier="S-1-5-32-544" />
</allowAccounts>
</net.pipe>
<diagnostics performanceCountersEnabled="true" />
</system.serviceModel.activation>’

$configurationNode = $xmlDoc.DocumentElement
$newConfig = $xmlDoc.ImportNode($config.DocumentElement, $true)
$configurationNode.AppendChild($newConfig)

$allowAccounts = Select-Xml "configuration/system.serviceModel.activation/net.tcp/allowAccounts/add" $xmlDoc
$allowAccounts | ForEach-Object {
$ConfiguredSid = $_.Node.Attributes.Item(0).Value
Write-Host "Found SID $ConfiguredSid"
if ($ConfiguredSid -eq $UserSid) {$UserSidFound = ‘true’}
$ConfigurationSet = ‘true’
}

}

#Add Service User Sid if needed
if ($UserSidFound -ne ‘true’) {
$nettcp = $xmlDoc.SelectSingleNode("configuration/system.serviceModel.activation/net.tcp/allowAccounts")
$addNode = $xmlDoc.CreateElement(‘add’)
$secIden = $xmlDoc.CreateAttribute(‘securityIdentifier’)
$secIden.Value = $UserSid
$addNode.Attributes.Append($secIden)

$nettcp.AppendChild($addNode)
$xmlDoc.Save($SMSvcHostPathConfig)
Write-Host "Configuration Updated"
#Restart Service if running
if ((Get-Service NetTcpPortSharing).Status -eq "Running") {Restart-Service NetTcpPortSharing -Force}
}

[/code]

This script will search for the SMSvcHost.exe.config file, load it and check to see if the NAV Service User is already allowed access.  If not then the config file is updated and saved.  This script must be executed with administrative privileges.

Perhaps this should be what I started with, but the question; why do we need this, should be answered.

First, modifying the startup mode to delayed start is done to make sure that all the required networking and database processes have been started before the NAV Service starts.  This is very important if the SQL Server is running on the same server.  On a dedicated NAV Service server this is not as important but still recommended.

Secondly, accessing a NAV Service in most cases requires changes to a firewall.  Either to open a specific port or setting up a NAT from a public interface.  To minimize the number of ports used also minimizes the networking setup and maintenance.  If different network permissions or network access is required I recommend using separate ports for the NAV Services.

Please add block possibility to Permission table

Yesterday I suggested to Microsoft an enhancement to the permission functionality.  That was an informal suggestion so I logged into Microsoft Connect and added a formal suggestion.

In table 2000000005 Permission we can assign permission to objects.  In the Classic Client we had the possibility to assign permissions to objects with the type System.  This is not working in the NAV 2013 (R2) client.

This causes a problem, for example a user with SUPER (Data) permission can delete a company from the database.

I suggest that a new option be added to fields 6, 7, 8, 9, 10 in the above table.
Current option string is ” ,Yes,Indirect”
The new option string would be ” ,Yes,Indirect,Blocked”

If an access type is blocked in any permission entry the access will be blocked even if there is access in another permission set.

I would for example add a line to the Permission table for the SUPER (Data) permission set that will block Insert, Modify and Delete for table 2000000006 Company.

Please help me by voting for the suggestion.

Using a .dll proxy for web services

I have now completed my first all-dotnet codeunit.  The codeunit uses a dll file that I created from the web service WDSL.  This makes the programming a lot easier.

This solution has a BLOB fields that stores both incoming and outgoing xml.  When I use a proxy dll I don’t build a xml document and I never handle xml documents.  Again dotnet has a solution.  I use xml serializer from the system.xml object.

[code]LOCAL PROCEDURE SerializeToXMLStream@1100408008(VAR Object@1100408002 : DotNet "’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Object" RUNONCLIENT;VAR NavOutstr@1100408004 : OutStream);
VAR
xmlSerializer@1100408000 : DotNet "’System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Xml.Serialization.XmlSerializer" RUNONCLIENT;
StreamWriter@1100408003 : DotNet "’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.IO.StreamWriter" RUNONCLIENT;
File@1100408001 : DotNet "’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.IO.File" RUNONCLIENT;
NavInstr@1100408005 : InStream;
BEGIN
IF ISNULL(Object) THEN EXIT;
StreamWriter := StreamWriter.StreamWriter(TempFileName);
xmlSerializer := xmlSerializer.XmlSerializer(Object.GetType);
xmlSerializer.Serialize(StreamWriter,Object);
StreamWriter.Close;
UPLOADINTOSTREAM(”,MagicPath,”,TempFileName,NavInstr);
COPYSTREAM(NavOutstr,NavInstr);
File.Delete(TempFileName);
END;[/code]

I can send the proxy object to this function and will get the xml into the BLOB field.

[code]CurrencyRates := StatementService.GetCurrencyRates(TypeRates,CREATEDATETIME(RatesDate,235959T));

WITH BankAccAction DO BEGIN
"Modified by User ID" := USERID;
"Modification Date and Time" := CURRENTDATETIME;
"Incoming Message".CREATEOUTSTREAM(OutStr);
SerializeToXMLStream(CurrencyRates,OutStr);
MODIFY;
COMMIT;
END;[/code]

This example is executed on the client since I am using certificates and web services security for the communication.

A DotNet Interop Soap Web Request

I am currently working on a solution that requires a Dynamics NAV client to communicate with Dynamics NAV web service.  This I have done before with the classic client and have used automation objects for the job.  Now I wanted to do this with dotnet only objects in the Role Tailored Client.  Took some time to put all things together but here it is.  This version is running the request from the client.

OBJECT Codeunit 50027 IC Addon Inbox WebService
{
  OBJECT-PROPERTIES
  {
    Date=09.04.14;
    Time=17:28:02;
    Modified=Yes;
    Version List=IC7.10;
  }
  PROPERTIES
  {
    OnRun=BEGIN
          END;

  }
  CODE
  {

    PROCEDURE LoadTransaction@1100408001(FromPartnerCode@1100408004 : Code[20];FromRespCenterCode@1100408005 : Code[10];ToPartnerCode@1100408006 : Code[20];ToRespCenterCode@1100408007 : Code[10];Transaction@1100408000 : BigText;PDFInvoice@1100408001 : BigText;PDFDetails@1100408002 : BigText;XMLInvoice@1100408003 : BigText;VAR ResponseMessage@1100408009 : Text) Success : Boolean;
    VAR
      Loader@1000000000 : Codeunit 50019;
      TransactionStream@1000000014 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.MemoryStream";
      PDFInvoiceStream@1000000016 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.MemoryStream";
      PDFDetailsStream@1000000015 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.MemoryStream";
      XMLInvoiceStream@1000000013 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.MemoryStream";
    BEGIN
      TransactionStream := TransactionStream.MemoryStream;
      PDFInvoiceStream := PDFInvoiceStream.MemoryStream;
      PDFDetailsStream := PDFDetailsStream.MemoryStream;
      XMLInvoiceStream := XMLInvoiceStream.MemoryStream;
      IF Transaction.LENGTH > 0 THEN
        Transaction.WRITE(TransactionStream);
      IF PDFInvoice.LENGTH > 0 THEN
        PDFInvoice.WRITE(PDFInvoiceStream);
      IF PDFDetails.LENGTH > 0 THEN
        PDFDetails.WRITE(PDFDetailsStream);
      IF XMLInvoice.LENGTH > 0 THEN
        XMLInvoice.WRITE(XMLInvoiceStream);

      Loader.SetProperties(
        FromPartnerCode,
        FromRespCenterCode,
        ToPartnerCode,
        ToRespCenterCode,
        TransactionStream,
        PDFInvoiceStream,
        PDFDetailsStream,
        XMLInvoiceStream,
        Transaction.LENGTH > 0,
        PDFInvoice.LENGTH > 0,
        PDFDetails.LENGTH > 0,
        XMLInvoice.LENGTH > 0);

      IF Loader.RUN THEN
        EXIT(TRUE)
      ELSE BEGIN
        ResponseMessage := GETLASTERRORTEXT;
        EXIT(FALSE);
      END;
    END;

    BEGIN
    END.
  }
}
OBJECT Codeunit 50028 IC Addon Web Service Client
{
  OBJECT-PROPERTIES
  {
    Date=08.03.15;
    Time=16:01:05;
    Modified=Yes;
    Version List=IC7.10.0432;
  }
  PROPERTIES
  {
    OnRun=BEGIN
          END;

  }
  CODE
  {
    VAR
      Text001@1000000019 : TextConst 'ENU=Succesfully delivered;ISL=Sending hepnaÐist';
      Text003@1100408001 : TextConst 'ENU=Error: %1\%2;ISL=St”Ðuvilla: %1\%2';
      Credential@1000000015 : DotNet "'System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.NetworkCredential";
      HttpWebRequest@1000000014 : DotNet "'System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.HttpWebRequest";
      HttpWebResponse@1000000013 : DotNet "'System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.WebResponse";
      HttpWebException@1000000017 : DotNet "'System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.WebException";
      MemoryStream@1000000012 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.MemoryStream";
      XMLRequestDoc@1000000011 : DotNet "'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlDocument";
      XMLResponseDoc@1000000010 : DotNet "'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlDocument";
      XMLProsInstr@1000000009 : DotNet "'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlProcessingInstruction";
      XMLElement1@1000000008 : DotNet "'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlElement";
      XMLElement2@1000000007 : DotNet "'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlElement";
      XMLElement3@1000000006 : DotNet "'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlElement";
      XMLNode4@1000000005 : DotNet "'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNode";
      XMLNsMgr@1000000004 : DotNet "'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNamespaceManager";
      Bytes@1000000003 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Array";
      String@1000000002 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.String";
      Convert@1000000001 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Convert";
      ServerFile@1000000000 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.File";
      NAVWebRequest@1000000018 : DotNet "'NAVWebRequest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f53f0925d26e1382'.NAVWebRequest.NAVWebRequest";
      RespCenter@1100408000 : Record 5714;
      CompanyInfo@1100408002 : Record 79;
      Log@1000000016 : Record 50009;
      FileMgt@1100408003 : Codeunit 419;
      WebServiceName@1100408008 : Text[1024];
      InStr@1100408005 : InStream;
      Text006@1100408007 : TextConst 'ENU=Export;ISL=Flytja £t';
      Text009@1100408006 : TextConst 'ENU=All Files (*.*)|*.*;ISL=Allar skr r (*.*)|*.*';

    PROCEDURE SendToPartner@1100408000(ICOutboxTrans@1100408000 : Record 414;ICPartner@1100408001 : Record 413;FileName@1100408002 : Text[250]);
    BEGIN
      ICPartner.TESTFIELD("Inbox Details");
      WebServiceName := FindWebServiceName(ICPartner."Inbox Details");

      WITH ICOutboxTrans DO BEGIN
        CALCFIELDS("PDF Document","XML Document","Details Document");

        IF "Responsibility Center" <> '' THEN BEGIN
          RespCenter.GET("Responsibility Center");
          RespCenter.TESTFIELD("IC Partner Code");
          CompanyInfo."IC Partner Code" := RespCenter."IC Partner Code";
        END ELSE BEGIN
          CompanyInfo.GET;
          CompanyInfo.TESTFIELD("IC Partner Code");
        END;

      END;

      SendTransactionToPartnerDotNet(ICOutboxTrans,ICPartner,FileName)
    END;

    LOCAL PROCEDURE FindWebServiceName@1100408002(URL@1100408000 : Text[1024]) WebServiceName : Text[1024];
    VAR
      i@1100408001 : Integer;
    BEGIN
      FOR i := 1 TO STRLEN(URL) DO
        IF COPYSTR(URL,i,1) = '/' THEN
          WebServiceName := COPYSTR(URL,i + 1);
    END;

    LOCAL PROCEDURE SendTransactionToPartnerDotNet@1100408003(ICOutboxTrans@1100408000 : Record 414;ICPartner@1100408001 : Record 413;FileName@1100408002 : Text[250]);
    VAR
      TempFile@1000000001 : File;
      TempFileName@1000000000 : Text[250];
      WebServiceUserID@1000000003 : Text[1024];
      OutStr@1000000002 : OutStream;
    BEGIN
      WITH ICOutboxTrans DO BEGIN

        Log.GET("Transaction No.");
        Log."Delivered Date and Time" := CURRENTDATETIME;
        Log."Delivered by User ID" := USERID;

        XMLRequestDoc := XMLResponseDoc.XmlDocument;
        XMLProsInstr := XMLRequestDoc.CreateProcessingInstruction('xml','version="1.0" encoding="utf-8"');
        XMLRequestDoc.AppendChild(XMLProsInstr);

        XMLElement1 := XMLRequestDoc.CreateElement('soap','Envelope','http://schemas.xmlsoap.org/soap/envelope/');
        XMLElement1.SetAttribute('xmlns:xsi','http://www.w3.org/2001/XMLSchema-instance');
        XMLElement1.SetAttribute('xmlns:xsd','http://www.w3.org/2001/XMLSchema');

        XMLElement2 := XMLRequestDoc.CreateElement('soap','Body', 'http://schemas.xmlsoap.org/soap/envelope/');
        XMLElement3 := XMLRequestDoc.CreateElement('LoadTransaction');
        XMLElement3.SetAttribute('xmlns',STRSUBSTNO('urn:microsoft-dynamics-schemas/codeunit/%1',WebServiceName));

        XMLNode4 := XMLRequestDoc.CreateElement('fromPartnerCode');
        XMLNode4.InnerText := CompanyInfo."IC Partner Code";
        XMLElement3.AppendChild(XMLNode4);

        XMLNode4 := XMLRequestDoc.CreateElement('fromRespCenterCode');
        IF ICPartner."Send Resp. Center Code" THEN
          XMLNode4.InnerText := "Responsibility Center";
        XMLElement3.AppendChild(XMLNode4);

        XMLNode4 := XMLRequestDoc.CreateElement('toPartnerCode');
        XMLNode4.InnerText := "IC Partner Code";
        XMLElement3.AppendChild(XMLNode4);

        XMLNode4 := XMLRequestDoc.CreateElement('toRespCenterCode');
        XMLNode4.InnerText := "IC Partner Resp. Center";

        XMLElement3.AppendChild(XMLNode4);

        XMLNode4 := XMLRequestDoc.CreateElement('transaction');
        XMLNode4.InnerText := Convert.ToBase64String(ServerFile.ReadAllBytes(FileName));
        XMLElement3.AppendChild(XMLNode4);
        TempFile.OPEN(FileName);
        TempFile.CREATEINSTREAM(InStr);
        Log.Transaction.CREATEOUTSTREAM(OutStr);
        COPYSTREAM(OutStr,InStr);
        TempFile.CLOSE;
        ServerFile.Delete(FileName);

        XMLNode4 := XMLRequestDoc.CreateElement('pDFInvoice');
        IF "PDF Document".HASVALUE THEN BEGIN
          "PDF Document".CREATEINSTREAM(InStr);
          TempFileName := FileMgt.ServerTempFileName('pdf');
          TempFile.CREATE(TempFileName);
          TempFile.CREATEOUTSTREAM(OutStr);
          COPYSTREAM(OutStr,InStr);
          TempFile.CLOSE;
          XMLNode4.InnerText := Convert.ToBase64String(ServerFile.ReadAllBytes(TempFileName));
          ServerFile.Delete(TempFileName);
        END;
        XMLElement3.AppendChild(XMLNode4);

        XMLNode4 := XMLRequestDoc.CreateElement('pDFDetails');
        IF "Details Document".HASVALUE THEN BEGIN
          "Details Document".CREATEINSTREAM(InStr);
          TempFileName := FileMgt.ServerTempFileName('pdf');
          TempFile.CREATE(TempFileName);
          TempFile.CREATEOUTSTREAM(OutStr);
          COPYSTREAM(OutStr,InStr);
          TempFile.CLOSE;
          XMLNode4.InnerText := Convert.ToBase64String(ServerFile.ReadAllBytes(TempFileName));
          ServerFile.Delete(TempFileName);
        END;
        XMLElement3.AppendChild(XMLNode4);

        XMLNode4 := XMLRequestDoc.CreateElement('xMLInvoice');
        IF "XML Document".HASVALUE THEN BEGIN
          "XML Document".CREATEINSTREAM(InStr);
          TempFileName := FileMgt.ServerTempFileName('xml');
          TempFile.CREATE(TempFileName);
          TempFile.CREATEOUTSTREAM(OutStr);
          COPYSTREAM(OutStr,InStr);
          TempFile.CLOSE;
          XMLNode4.InnerText := Convert.ToBase64String(ServerFile.ReadAllBytes(TempFileName));
          ServerFile.Delete(TempFileName);
        END;
        XMLElement3.AppendChild(XMLNode4);

        XMLNode4 := XMLRequestDoc.CreateElement('responseMessage');
        XMLElement3.AppendChild(XMLNode4);
        XMLElement2.AppendChild(XMLElement3);
        XMLElement1.AppendChild(XMLElement2);
        XMLRequestDoc.AppendChild(XMLElement1);

        HttpWebRequest := HttpWebRequest.Create(ICPartner."Inbox Details");
        HttpWebRequest.Timeout := 30000;
        WebServiceUserID := ICPartner.GetUserID;
        IF WebServiceUserID = '' THEN
          HttpWebRequest.UseDefaultCredentials(TRUE)
        ELSE BEGIN
          HttpWebRequest.UseDefaultCredentials(FALSE);
          Credential := Credential.NetworkCredential;
          Credential.UserName := WebServiceUserID;
          Credential.Password := ICPartner.GetPassword;
          Credential.Domain := ICPartner.GetDomain;
          HttpWebRequest.Credentials := Credential;
        END;
        HttpWebRequest.Method := 'POST';
        HttpWebRequest.ContentType := 'text/xml; charset=utf-8';
        HttpWebRequest.Accept := 'text/xml';
        HttpWebRequest.Headers.Add('SOAPAction','LoadTransaction');
        MemoryStream := HttpWebRequest.GetRequestStream;
        XMLRequestDoc.Save(MemoryStream);
        MemoryStream.Flush;
        MemoryStream.Close;

        NAVWebRequest := NAVWebRequest.NAVWebRequest;
        IF NOT NAVWebRequest.doRequest(HttpWebRequest,HttpWebException,HttpWebResponse) THEN BEGIN
          Log.Delivered := FALSE;
          Log.SetMessage(HttpWebException.Message);
          Log.MODIFY;
          COMMIT;
          ERROR(Text003,HttpWebException.Status.ToString,HttpWebException.Message);
        END;

        MemoryStream := HttpWebResponse.GetResponseStream;
        XMLResponseDoc := XMLResponseDoc.XmlDocument;
        XMLResponseDoc.Load(MemoryStream);
        MemoryStream.Flush;
        MemoryStream.Close;

        XMLNsMgr := XMLNsMgr.XmlNamespaceManager(XMLResponseDoc.NameTable);
        XMLNsMgr.AddNamespace('urn',STRSUBSTNO('urn:microsoft-dynamics-schemas/codeunit/%1',WebServiceName));
        XMLNode4 := XMLResponseDoc.SelectSingleNode('//urn:return_value',XMLNsMgr);

        IF UPPERCASE(XMLNode4.InnerText) = 'FALSE' THEN BEGIN
          XMLNode4 :=  XMLResponseDoc.SelectSingleNode('//urn:responseMessage',XMLNsMgr);
          Log.Delivered := FALSE;
          Log.SetMessage(XMLNode4.InnerText);
          Log.MODIFY;
          COMMIT;
          ERROR(XMLNode4.InnerText);
        END;

        Log.Delivered := TRUE;
        Log.SetMessage(Text001);
        Log.MODIFY;
        COMMIT;

      END;
    END;

    EVENT XMLResponseDoc@1000000010::NodeInserting@93(sender@1000000001 : Variant;e@1000000000 : DotNet "'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNodeChangedEventArgs");
    BEGIN
    END;

    EVENT XMLResponseDoc@1000000010::NodeInserted@94(sender@1000000001 : Variant;e@1000000000 : DotNet "'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNodeChangedEventArgs");
    BEGIN
    END;

    EVENT XMLResponseDoc@1000000010::NodeRemoving@95(sender@1000000001 : Variant;e@1000000000 : DotNet "'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNodeChangedEventArgs");
    BEGIN
    END;

    EVENT XMLResponseDoc@1000000010::NodeRemoved@96(sender@1000000001 : Variant;e@1000000000 : DotNet "'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNodeChangedEventArgs");
    BEGIN
    END;

    EVENT XMLResponseDoc@1000000010::NodeChanging@97(sender@1000000001 : Variant;e@1000000000 : DotNet "'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNodeChangedEventArgs");
    BEGIN
    END;

    EVENT XMLResponseDoc@1000000010::NodeChanged@98(sender@1000000001 : Variant;e@1000000000 : DotNet "'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNodeChangedEventArgs");
    BEGIN
    END;

    EVENT XMLRequestDoc@1000000011::NodeInserting@93(sender@1000000001 : Variant;e@1000000000 : DotNet "'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNodeChangedEventArgs");
    BEGIN
    END;

    EVENT XMLRequestDoc@1000000011::NodeInserted@94(sender@1000000001 : Variant;e@1000000000 : DotNet "'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNodeChangedEventArgs");
    BEGIN
    END;

    EVENT XMLRequestDoc@1000000011::NodeRemoving@95(sender@1000000001 : Variant;e@1000000000 : DotNet "'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNodeChangedEventArgs");
    BEGIN
    END;

    EVENT XMLRequestDoc@1000000011::NodeRemoved@96(sender@1000000001 : Variant;e@1000000000 : DotNet "'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNodeChangedEventArgs");
    BEGIN
    END;

    EVENT XMLRequestDoc@1000000011::NodeChanging@97(sender@1000000001 : Variant;e@1000000000 : DotNet "'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNodeChangedEventArgs");
    BEGIN
    END;

    EVENT XMLRequestDoc@1000000011::NodeChanged@98(sender@1000000001 : Variant;e@1000000000 : DotNet "'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNodeChangedEventArgs");
    BEGIN
    END;

    BEGIN
    END.
  }
}

OBJECT Codeunit 50019 IC Addon Load Transaction
{
  OBJECT-PROPERTIES
  {
    Date=02.05.14;
    Time=10:12:10;
    Modified=Yes;
    Version List=IC7.10;
  }
  PROPERTIES
  {
    OnRun=BEGIN
            LoadTransaction;
          END;

  }
  CODE
  {
    VAR
      Text001@1100408000 : TextConst 'ENU=IC Partner Code %1 not found;ISL=Mf. f‚lagak¢ti %1 finnst ekki';
      Text002@1100408001 : TextConst 'ENU=Responsibility Center Code mismatch, %1 <> %2;ISL=µbyrgÐast”Ðvark¢ti stemmir ekki, %1 <> %2';
      Convert@1100408004 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Convert";
      DocumentFile@1100408003 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.File";
      Bytes@1100408010 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Array";
      MemoryStream@1100408008 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.MemoryStream";
      FileMgt@1100408009 : Codeunit 419;
      TempFile@1100408007 : File;
      InStr@1000000011 : InStream;
      OutStr@1000000010 : OutStream;
      TempFileName@1100408006 : Text[1024];
      Text003@1100408011 : TextConst 'ENU=No data received;ISL=Engin g”gn m¢ttekin';
      Text004@1100408012 : TextConst 'ENU=Transaction no. %1 is already imported;ISL=F‘rsla nr. %1 er çegar innflutt';
      Text005@1000000000 : TextConst 'ENU=From Partner Code Error in Transaction, %1 <> %2;ISL=Fr  mf. f‚lagak¢ta villa ¡ f‘rslu, %1 <> %2';
      Text006@1000000001 : TextConst 'ENU=To Partner Code Error in Transaction, %1 <> %2;ISL=Til mf. f‚lagak¢ta villa ¡ f‘rslu, %1 <> %2';
      FromPartnerCode@1000000009 : Code[20];
      FromRespCenterCode@1000000008 : Code[10];
      ToPartnerCode@1000000007 : Code[20];
      ToRespCenterCode@1000000006 : Code[10];
      Transaction@1000000005 : BigText;
      PDFInvoice@1000000004 : BigText;
      PDFDetails@1000000003 : BigText;
      XMLInvoice@1000000002 : BigText;

    LOCAL PROCEDURE LoadTransaction@1100408001();
    VAR
      ICPartner@1100408010 : Record 413;
      TempBlob@1100408024 : TEMPORARY Record 99008535;
      TempICOutboxTrans@1100408020 : TEMPORARY Record 414;
      TempICOutBoxJnlLine@1100408019 : TEMPORARY Record 415;
      TempICIOBoxJnlDim@1100408018 : TEMPORARY Record 423;
      TempICOutBoxSalesHdr@1100408017 : TEMPORARY Record 426;
      TempICOutBoxSalesLine@1100408016 : TEMPORARY Record 427;
      TempICOutBoxPurchHdr@1100408015 : TEMPORARY Record 428;
      TempICOutBoxPurchLine@1100408014 : TEMPORARY Record 429;
      TempICDocDim@1100408013 : TEMPORARY Record 442;
      ICInboxTransaction@1100408022 : Record 418;
      ICInboxTransaction2@1100408032 : Record 418;
      ICInboxJnlLine@1100408030 : Record 419;
      ICInboxSalesHdr@1100408029 : Record 434;
      ICInboxSalesLine@1100408028 : Record 435;
      ICInboxPurchHdr@1100408027 : Record 436;
      ICInboxPurchLine@1100408026 : Record 437;
      ICInboxJnlLineDim@1100408025 : Record 423;
      ICInboxDocDim@1100408023 : Record 442;
      HandledICInboxTransaction@1000000000 : Record 420;
      ICInboxOutboxMgt@1100408021 : Codeunit 427;
      FromICPartnerCode@1100408012 : Code[20];
      ToICPartnerCode@1100408011 : Code[20];
      ICOutboxExportXML@1100408008 : XMLport 12;
      NewTableID@1100408031 : Integer;
    BEGIN
      IF NOT ICPartner.GET(FromPartnerCode) THEN
        ERROR(Text001,FromPartnerCode);

      IF ICPartner."Responsibility Center" <> FromRespCenterCode THEN
        ERROR(Text002,ICPartner."Responsibility Center",FromRespCenterCode);

      IF NOT ICPartner.GET(ToPartnerCode) THEN
        ERROR(Text001,ToPartnerCode);

      IF ICPartner."Responsibility Center" <> ToRespCenterCode THEN
        ERROR(Text002,ICPartner."Responsibility Center",ToRespCenterCode);

      IF Transaction.LENGTH > 0 THEN BEGIN
        Bytes := Convert.FromBase64String(Transaction);
        MemoryStream := MemoryStream.MemoryStream(Bytes);
        TempBlob.Blob.CREATEOUTSTREAM(OutStr);
        MemoryStream.WriteTo(OutStr);
        TempBlob.Blob.CREATEINSTREAM(InStr);

        ICOutboxExportXML.SETSOURCE(InStr);
        ICOutboxExportXML.IMPORT;
        ICOutboxExportXML.GetICOutboxTrans(TempICOutboxTrans);
        ICOutboxExportXML.GetICOutBoxJnlLine(TempICOutBoxJnlLine);
        ICOutboxExportXML.GetICIOBoxJnlDim(TempICIOBoxJnlDim);
        ICOutboxExportXML.GetICOutBoxSalesHdr(TempICOutBoxSalesHdr);
        ICOutboxExportXML.GetICOutBoxSalesLine(TempICOutBoxSalesLine);
        ICOutboxExportXML.GetICOutBoxPurchHdr(TempICOutBoxPurchHdr);
        ICOutboxExportXML.GetICOutBoxPurchLine(TempICOutBoxPurchLine);
        ICOutboxExportXML.GetICSalesDocDim(TempICDocDim);
        ICOutboxExportXML.GetICSalesDocLineDim(TempICDocDim);
        ICOutboxExportXML.GetICPurchDocDim(TempICDocDim);
        ICOutboxExportXML.GetICPurchDocLineDim(TempICDocDim);
        FromICPartnerCode := ICOutboxExportXML.GetFromICPartnerCode;
        ToICPartnerCode := ICOutboxExportXML.GetToICPartnerCode;

        TempICOutBoxSalesHdr.MODIFYALL("Responsibility Center",FromRespCenterCode);
        TempICOutBoxSalesHdr.MODIFYALL("IC Partner Resp. Center",ToRespCenterCode);
        TempICOutBoxPurchHdr.MODIFYALL("Responsibility Center",FromRespCenterCode);
        TempICOutBoxPurchHdr.MODIFYALL("IC Partner Resp. Center",ToRespCenterCode);

        IF FromICPartnerCode <> FromPartnerCode THEN
          ERROR(Text005,FromICPartnerCode,FromPartnerCode);

        IF ToICPartnerCode <> ToPartnerCode THEN
          ERROR(Text006,ToICPartnerCode,ToPartnerCode);

        ICInboxTransaction2.SETRANGE("Transaction No.",TempICOutboxTrans."Transaction No.");
        ICInboxTransaction2.SETRANGE("IC Partner Code",FromICPartnerCode);
        ICInboxTransaction2.SETRANGE("Transaction Source",TempICOutboxTrans."Transaction Source");
        IF ICInboxTransaction2.FINDFIRST THEN
          ERROR(Text004,TempICOutboxTrans."Transaction No.");

        HandledICInboxTransaction.SETRANGE("Transaction No.",TempICOutboxTrans."Transaction No.");
        HandledICInboxTransaction.SETRANGE("IC Partner Code",FromICPartnerCode);
        HandledICInboxTransaction.SETRANGE("Transaction Source",TempICOutboxTrans."Transaction Source");
        IF HandledICInboxTransaction.FINDFIRST THEN
          ERROR(Text004,TempICOutboxTrans."Transaction No.");

        IF TempICOutboxTrans.FIND('-') THEN BEGIN
          ICInboxOutboxMgt.OutboxTransToInbox(TempICOutboxTrans,ICInboxTransaction,FromICPartnerCode);

          TempICOutBoxJnlLine.SETRANGE("Transaction No.",TempICOutboxTrans."Transaction No.");
          TempICOutBoxJnlLine.SETRANGE("IC Partner Code",TempICOutboxTrans."IC Partner Code");
          TempICOutBoxJnlLine.SETRANGE("Transaction Source",TempICOutboxTrans."Transaction Source");
          IF TempICOutBoxJnlLine.FIND('-') THEN
            REPEAT
              ICInboxOutboxMgt.OutboxJnlLineToInbox(ICInboxTransaction,TempICOutBoxJnlLine,ICInboxJnlLine);
              TempICIOBoxJnlDim.SETRANGE("Transaction No.",TempICOutboxTrans."Transaction No.");
              TempICIOBoxJnlDim.SETRANGE("IC Partner Code",TempICOutboxTrans."IC Partner Code");
              TempICIOBoxJnlDim.SETRANGE("Transaction Source",TempICOutboxTrans."Transaction Source");
              TempICIOBoxJnlDim.SETRANGE("Line No.",ICInboxJnlLine."Line No.");
              IF TempICIOBoxJnlDim.FIND('-') THEN
                REPEAT
                  ICInboxOutboxMgt.OutboxJnlLineDimToInbox(
                    ICInboxJnlLine,TempICIOBoxJnlDim,ICInboxJnlLineDim,DATABASE::"IC Inbox Jnl. Line");
                UNTIL TempICIOBoxJnlDim.NEXT = 0;
            UNTIL TempICOutBoxJnlLine.NEXT = 0;

          TempICOutBoxSalesHdr.SETRANGE("IC Transaction No.",TempICOutboxTrans."Transaction No.");
          TempICOutBoxSalesHdr.SETRANGE("IC Partner Code",TempICOutboxTrans."IC Partner Code");
          TempICOutBoxSalesHdr.SETRANGE("Transaction Source",TempICOutboxTrans."Transaction Source");
          IF TempICOutBoxSalesHdr.FIND('-') THEN
            REPEAT
              ICInboxOutboxMgt.OutboxSalesHdrToInbox(ICInboxTransaction,TempICOutBoxSalesHdr,ICInboxPurchHdr);
            UNTIL TempICOutBoxSalesHdr.NEXT = 0;

          TempICOutBoxSalesLine.SETRANGE("IC Transaction No.",TempICOutboxTrans."Transaction No.");
          TempICOutBoxSalesLine.SETRANGE("IC Partner Code",TempICOutboxTrans."IC Partner Code");
          TempICOutBoxSalesLine.SETRANGE("Transaction Source",TempICOutboxTrans."Transaction Source");
          IF TempICOutBoxSalesLine.FIND('-') THEN BEGIN
            REPEAT
              ICInboxOutboxMgt.OutboxSalesLineToInbox(ICInboxTransaction,TempICOutBoxSalesLine,ICInboxPurchLine);
            UNTIL TempICOutBoxSalesLine.NEXT = 0;
            ICInboxPurchLine.SETRANGE("IC Transaction No.",ICInboxPurchHdr."IC Transaction No.");
            ICInboxPurchLine.SETRANGE("IC Partner Code",ICInboxPurchHdr."IC Partner Code");
            ICInboxPurchLine.SETRANGE("Transaction Source",ICInboxPurchHdr."Transaction Source");
            ICInboxPurchLine.SETRANGE("VAT Base Amount",-0.5,0.5);
            ICInboxPurchLine.CALCSUMS("Amount Including VAT");
            ICInboxPurchHdr."Payable Rounding Amount" := ICInboxPurchLine."Amount Including VAT";
            ICInboxPurchLine.SETRANGE("VAT Base Amount");
            ICInboxPurchLine.CALCSUMS("Amount Including VAT","VAT Base Amount");
            ICInboxPurchHdr."Payable Amount" := ICInboxPurchLine."Amount Including VAT";
            ICInboxPurchHdr."Line Extension Amount" := ICInboxPurchLine."VAT Base Amount" - ICInboxPurchHdr."Payable Rounding Amount";
            ICInboxPurchHdr."Tax Exclusive Amount" := ICInboxPurchHdr."Line Extension Amount";
            ICInboxPurchHdr."Tax Inclusive Amount" := ICInboxPurchLine."Amount Including VAT" - ICInboxPurchHdr."Payable Rounding Amount";
            ICInboxPurchHdr."Tax Amount" :=  ICInboxPurchHdr."Tax Inclusive Amount" - ICInboxPurchHdr."Tax Exclusive Amount";
            ICInboxPurchHdr.MODIFY;
          END;

          TempICOutBoxPurchHdr.SETRANGE("IC Transaction No.",TempICOutboxTrans."Transaction No.");
          TempICOutBoxPurchHdr.SETRANGE("IC Partner Code",TempICOutboxTrans."IC Partner Code");
          TempICOutBoxPurchHdr.SETRANGE("Transaction Source",TempICOutboxTrans."Transaction Source");
          IF TempICOutBoxPurchHdr.FIND('-') THEN
            REPEAT
              ICInboxOutboxMgt.OutboxPurchHdrToInbox(ICInboxTransaction,TempICOutBoxPurchHdr,ICInboxSalesHdr);
            UNTIL TempICOutBoxPurchHdr.NEXT = 0;

          TempICOutBoxPurchLine.SETRANGE("IC Transaction No.",TempICOutboxTrans."Transaction No.");
          TempICOutBoxPurchLine.SETRANGE("IC Partner Code",TempICOutboxTrans."IC Partner Code");
          TempICOutBoxPurchLine.SETRANGE("Transaction Source",TempICOutboxTrans."Transaction Source");
          IF TempICOutBoxPurchLine.FIND('-') THEN
            REPEAT
              ICInboxOutboxMgt.OutboxPurchLineToInbox(ICInboxTransaction,TempICOutBoxPurchLine,ICInboxSalesLine);
            UNTIL TempICOutBoxPurchLine.NEXT = 0;

          TempICDocDim.SETRANGE("Transaction No.",TempICOutboxTrans."Transaction No.");
          TempICDocDim.SETRANGE("IC Partner Code",TempICOutboxTrans."IC Partner Code");
          TempICDocDim.SETRANGE("Transaction Source",TempICOutboxTrans."Transaction Source");
          IF TempICDocDim.FIND('-') THEN
            REPEAT
              CASE TempICDocDim."Table ID" OF
                DATABASE::"IC Outbox Sales Header": NewTableID := DATABASE::"IC Inbox Purchase Header";
                DATABASE::"IC Outbox Sales Line": NewTableID := DATABASE::"IC Inbox Purchase Line";
                DATABASE::"IC Outbox Purchase Header": NewTableID := DATABASE::"IC Inbox Sales Header";
                DATABASE::"IC Outbox Purchase Line": NewTableID := DATABASE::"IC Inbox Sales Line";
              END;
              ICInboxOutboxMgt.OutboxDocDimToInbox(
                TempICDocDim,ICInboxDocDim,NewTableID,FromICPartnerCode,ICInboxTransaction."Transaction Source");
            UNTIL TempICDocDim.NEXT = 0;
        END;

        ICInboxTransaction."Responsibility Center" := ToRespCenterCode;
        ICInboxTransaction."IC Partner Resp. Center" := FromRespCenterCode;

        IF XMLInvoice.LENGTH > 0 THEN BEGIN
          Bytes := Convert.FromBase64String(XMLInvoice);
          MemoryStream := MemoryStream.MemoryStream(Bytes);
          ICInboxTransaction."XML Document".CREATEOUTSTREAM(OutStr);
          MemoryStream.WriteTo(OutStr);
        END;

        IF PDFInvoice.LENGTH > 0 THEN BEGIN
          Bytes := Convert.FromBase64String(PDFInvoice);
          MemoryStream := MemoryStream.MemoryStream(Bytes);
          ICInboxTransaction."PDF Document".CREATEOUTSTREAM(OutStr);
          MemoryStream.WriteTo(OutStr);
        END;

        IF PDFDetails.LENGTH > 0 THEN BEGIN
          Bytes := Convert.FromBase64String(PDFDetails);
          MemoryStream := MemoryStream.MemoryStream(Bytes);
          ICInboxTransaction."Details Document".CREATEOUTSTREAM(OutStr);
          MemoryStream.WriteTo(OutStr);
        END;

        ICInboxTransaction.MODIFY;
      END ELSE
        ERROR(Text003);
    END;

    PROCEDURE SetProperties@1000000000(VAR SetFromPartnerCode@1000000007 : Code[20];VAR SetFromRespCenterCode@1000000006 : Code[10];VAR SetToPartnerCode@1000000005 : Code[20];VAR SetToRespCenterCode@1000000004 : Code[10];VAR SetTransaction@1000000003 : InStream;VAR SetPDFInvoice@1000000002 : InStream;VAR SetPDFDetails@1000000001 : InStream;VAR SetXMLInvoice@1000000000 : InStream;TransactionHasValue@1000000011 : Boolean;PDFInvoiceHasValue@1000000010 : Boolean;PDFDetailsHasValue@1000000009 : Boolean;XMLInvoiceHasValue@1000000008 : Boolean);
    BEGIN
      FromPartnerCode := SetFromPartnerCode;
      FromRespCenterCode := SetFromRespCenterCode;
      ToPartnerCode := SetToPartnerCode;
      ToRespCenterCode := SetToRespCenterCode;
      IF TransactionHasValue THEN
        Transaction.READ(SetTransaction);
      IF PDFInvoiceHasValue THEN
        PDFInvoice.READ(SetPDFInvoice);
      IF PDFDetailsHasValue THEN
        PDFDetails.READ(SetPDFDetails);
      IF XMLInvoiceHasValue THEN
        XMLInvoice.READ(SetXMLInvoice);
    END;

    BEGIN
    END.
  }
}

 

Testing your Dynamics NAV Web Service

I am building a web service for one of my clients and another company is using this web service for an aspx web site.  I realized that I needed to test my web service before I can deliver it to that company.  So, I created a test codeunit for the job.

First I downloaded the universal XML import/export tool from Mibuso.  Then I added a function to the table 60000 XML Buffer that is in the above tool.
[code htmlscript=”false”]Read(VAR DOMDoc : Automation "’Microsoft XML, v6.0′.DOMDocument")
DELETEALL;
DOMNode := DOMDoc.documentElement;
Import2(DOMNode,1);
IF FINDFIRST THEN;[/code]
Next I create a read function in my test codeunit for every function in the web service.  Here is an example.
[code htmlscript=”false”]GetFarmerTankEntryAverageYW(FarmerID : Integer;MinYear : Integer;MaxYear : Integer;MinWeekNo : Integer;MaxWeekNo : Integer)
GetSetup;
CREATE(XMLDoc,TRUE,FALSE);

XMLProsInstr := XMLDoc.createProcessingInstruction(‘xml’,’version="1.0" encoding="utf-8"’);
XMLDoc.appendChild(XMLProsInstr);

CreateEnvelope(XMLElement1);
XMLElement2 := XMLDoc.createElement(‘soap:Body’);
XMLElement3 := XMLDoc.createElement(‘GetFarmerTankEntryAverageYW’);
XMLElement3.setAttribute(‘xmlns’,’urn:microsoft-dynamics-schemas/codeunit/RMWeb’);
CreateElement(XMLElement3, ‘farmerID’, FORMAT(FarmerID,0,9), ”, ”);
CreateElement(XMLElement3, ‘minYear’, FORMAT(MinYear,0,9), ”, ”);
CreateElement(XMLElement3, ‘maxYear’, FORMAT(MaxYear,0,9), ”, ”);
CreateElement(XMLElement3, ‘minWeekNo’, FORMAT(MinWeekNo,0,9), ”, ”);
CreateElement(XMLElement3, ‘maxWeekNo’, FORMAT(MaxWeekNo,0,9), ”, ”);
CreateElement(XMLElement3, ‘tankEntryXML’, ”, ”, ”);
XMLElement2.appendChild(XMLElement3);
XMLElement1.appendChild(XMLElement2);
XMLDoc.appendChild(XMLElement1);

WinHTTP.open(‘POST’,ServiceURL,FALSE,UserName,Password);
WinHTTP.setRequestHeader(‘Content-Type’,’text/xml; charset=utf-8′);
WinHTTP.setRequestHeader(‘SOAPAction’,’GetFarmerTankEntryAverageYW’);
WinHTTP.send(XMLDoc);

IF WinHTTP.status <> 200 THEN
ERROR(Text003,WinHTTP.status,WinHTTP.statusText);

XMLResponseDoc.load(WinHTTP.responseXML);
DisplayDocument(XMLResponseDoc);[/code]
This will use the XML Buffer to read the response document and display the result.  The Text Constant Text003 contains
[code htmlscript=”false”]ENU=Status error %1 %2;ISL=Stöðuvilla %1 %2[/code]
and the four functions used here contain
[code htmlscript=”false”]DisplayDocument(VAR XMLDoc : Automation "’Microsoft XML, v6.0′.DOMDocument")
XMLBuffer.Read(XMLDoc);
COMMIT;
FORM.RUNMODAL(FORM::"XML Buffer");

CreateEnvelope(VAR InElement : Automation "’Microsoft XML, v6.0′.IXMLDOMElement")
InElement := XMLRequestDoc.createElement(‘soap:Envelope’);
InElement.setAttribute(‘xmlns:soap’,’http://schemas.xmlsoap.org/soap/envelope/’);
InElement.setAttribute(‘xmlns:xsi’,’http://www.w3.org/2001/XMLSchema-instance’);
InElement.setAttribute(‘xmlns:xsd’,’http://www.w3.org/2001/XMLSchema’);

CreateElement(VAR InElement : Automation "’Microsoft XML, v6.0′.IXMLDOMElement";InNodeName : Text[50];InNodeValue : Text[250];InAttribu
TempElement := XMLRequestDoc.createElement(InNodeName);
TempElement.nodeTypedValue(InNodeValue);
IF InAttributeName <> ” THEN
TempElement.setAttribute(InAttributeName,InAttributeValue);
InElement.appendChild(TempElement);

GetSetup()
ServiceURL := ‘http://gunnar.dynamics.is:7047/DynamicsNAV/WS/CRONUS/Codeunit/WebService’;
UserName := ‘<Domain\User>’;
Password := ‘<Password>’;
IF ISCLEAR(WinHTTP) THEN
CREATE(WinHTTP,TRUE,FALSE);
IF ISCLEAR(XMLResponseDoc) THEN
CREATE(XMLResponseDoc,TRUE,FALSE);[/code]

WinHTTP and RTC Client

I have been using the automation ‘Microsoft XML, v6.0’.XMLHTTP to communicate with web services and web sites.  I have been experiencing a problem with this automation when running in Role Tailored Client.  The solution has been to use the automation ‘Microsoft XML, v6.0’.ServerXMLHTTP when running in the service tier.
[code htmlscript=”false”]IF ISSERVICETIER THEN BEGIN
IF ISCLEAR(WinHTTPServer) THEN
CREATE(WinHTTPServer,TRUE,FALSE);
WinHTTPServer.open(‘GET’,URL,FALSE);
WinHTTPServer.send(”);

IF WinHTTPServer.status <> 200 THEN
ERROR(Text007,WinHTTPServer.status,WinHTTPServer.statusText);

DOMDocument.load(WinHTTPServer.responseXML);
CLEAR(WinHTTPServer);
END ELSE BEGIN
IF ISCLEAR(WinHTTP) THEN
CREATE(WinHTTP,TRUE,FALSE);
WinHTTP.open(‘GET’,URL,FALSE);
WinHTTP.send(”);

IF WinHTTP.status <> 200 THEN
ERROR(Text007,WinHTTP.status,WinHTTP.statusText);

DOMDocument.load(WinHTTP.responseXML);
CLEAR(WinHTTP);
END;[/code]
Where Error string Text007 is “Status error %1 %2”.

WSDL Code Generator

On several occasions I have needed to create a code to communicate with Soap Web Services. The manual labor in creating the functions and XML Ports is something that I would like to be rid of. On that node I created a Batch that does most of the job for me.

Execute this and you will be asked to save the “Web Service Objects.txt” file to your computer and from there you will be able to import the file and continue the work.

You will get XMP Ports for every method, both the request and response.  You will also get a Codeunit with a function for every method and the necessary functions to handle the web service communication.

What is left is for you to connect your data to the functions and the XML Ports.

This is not fully tested, so any updates would be appreciated.

Import WSDL (Updated 2012-12-08)

NAV Web Service and PHP

I wanted to replace MySQL with NAV Web Service as a data source for my PHP web site.  I started to look around and found all I needed in Freddys Blog.  First post is about changes in authentication method of the web service, the later post displays the code needed to connect to the web service with php.

I have an php web on my Windows Home Server and needed to activate two more extensions to get things working.

Here are the php files that I used – PHP consume NAV Web Service