We use a session cookie as anonymous ID for your visit. View our privacy policy to learn more.

Welcome Guest : please login to be able to post Search | Active Topics | Log In | Register

CRUD operations in .NET Options · View
Feng Lin
Posted: Thursday, May 19, 2011 2:25:12 PM
Rank: Member

Joined: 5/17/2011
Posts: 1
Is there any sample code in C#\VB.NET to perform CRUD operations using http mothods (GET, POST, PUT, DELETE) against a resource, e.g. Customer?

Many thanks.
Abraham Sunny
Posted: Thursday, May 26, 2011 7:28:11 AM
Rank: Member

Joined: 2/4/2010
Posts: 4
Any help in SData for beginners?
Darron Cockram
Posted: Friday, June 03, 2011 4:35:47 AM
Rank: Advanced Member

Joined: 10/13/2010
Posts: 117
I'm currently working on developing some sample code that demonstrates using Sdata. No ETA on when this might be made available yet, or in what form, but Developer Support should be your first port of call in the mean time.

As this is something I am currently working on I would be very interested in hearing if there are any specific examples you'd be interested in seeing.
Darron Cockram
Posted: Friday, June 03, 2011 4:41:50 AM
Rank: Advanced Member

Joined: 10/13/2010
Posts: 117
There is a simple example of a GET operation using JavaScript available that can be downloaded from here: http://sdata.sage.com/downloads/SDataJavascriptSample.zip

The example uses the depreciated 'SDO' contract for Accounts 50 but should still work and would be straightforward enough to translate to C# or VB .NET.
Serge Bourque
Posted: Wednesday, July 13, 2011 4:23:44 PM
Rank: Member

Joined: 7/13/2011
Posts: 1
.net c# example

private void button1_Click(object sender, EventArgs e)
{
WebRequest req = WebRequest.Create("https://sdata.showcase.sage.com/sdata/accounts50/SDO/-/Customers");
req.Method = "GET";
req.ContentType = "application/atom+xml";
req.Credentials = new NetworkCredential("MANAGER", "");
WebResponse res= req.GetResponse();
StreamReader sr = new StreamReader(res.GetResponseStream());
XmlDocument doc = new XmlDocument();
doc.LoadXml(sr.ReadToEnd());
XmlNodeList l1 = doc.DocumentElement.GetElementsByTagName("entity:customer");
StringBuilder x = new StringBuilder();

for (int i = 0; i < l1.Count; i++)
{
x.Append(l1[i].ChildNodes[1].InnerText + "<br />");
x.Append(l1[i].ChildNodes[2].InnerText + "<br />");
x.Append(l1[i].ChildNodes[3].InnerText + "<br />");
x.Append(l1[i].ChildNodes[4].InnerText + "<br />");
x.Append(l1[i].ChildNodes[5].InnerText + "<br />");
x.Append("---------------------------------------------<br /><br />");
}
webBrowser1.DocumentText = x.ToString();//doc.InnerXml;
res.Close();
}
Darron Cockram
Posted: Thursday, July 14, 2011 1:26:19 PM
Rank: Advanced Member

Joined: 10/13/2010
Posts: 117
That's a nice simple example, the only thing to watch out for is the SDO contract has been discontinued for Accounts 50. Try using the GCRM contract instead - https://sdata.showcase.sage.com/sdata/accounts50/GCRM/-/tradingAccounts.

There are also some helper classes available to make your life easier with building the request and parsing the XML. I'll try to get an example using these posted soon.
Brian Mcnally
Posted: Sunday, January 22, 2012 6:53:52 AM
Rank: Member

Joined: 1/1/2012
Posts: 15
Has anyone got a POST example they could share? (Ideally in C# using GCRM! ;-)
Darron Cockram
Posted: Monday, January 23, 2012 3:41:34 AM
Rank: Advanced Member

Joined: 10/13/2010
Posts: 117
Our developer support team are busy working on code samples and other supporting documentation at the moment but in the mean time here's a simple example of a console application that demonstrates a POST operation.

NOTE: This sample makes use of the helper classes available in the Sage Integration Framework (SIF) as well as the GCRM implementation in the Sage 50 Accounts Sdata adapter. To run this code sample you will need to have Sage 50 Accounts 2012 installed locally and add references to

C:\Program Files\Sage\Assemblies\SData\Sage.Common.Syndication.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Integration.Accounts50.SDO.Adapter.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Integration.Accounts50.SDO.Adapter.GCRM.Feeds.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Integration.Client.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Integration.Server.Model.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Utilities.dll

(If you're on an x64 version of Windows amend those paths to C:\Program Files (x86)\....)

Code:

// Create a new instance of a trading account
tradingAccountFeedEntry account = new tradingAccountFeedEntry();

// Prompt for a customer name to use
Console.WriteLine("Please a name for the new customer:");

// Update the name
account.name = Console.ReadLine();

// required field
account.customerSupplierFlag = "Customer";

// Build the request URI. This example assumes that the Accounts 50 GCRM contract implementation is available
SDataUri uri = new SDataUri();
uri.BuildLocalPath("Accounts50", "GCRM", "-", "tradingAccounts");

// Build an create request.
// NOTE a create is an HTTP POST
SDataRequest createRequest = new SDataRequest(uri.Uri, account, Sage.Integration.Messaging.Model.RequestVerb.POST);
createRequest.AllowPromptForCredentials = false;
// Alter the following two lines to use the appropriate values for your installation
createRequest.Username = "<username>";
createRequest.Password = "<password>";

// Submit the create request
//createRequest.Send();
tradingAccountFeedEntry newAccount = new tradingAccountFeedEntry();
createRequest.RequestFeedEntry<tradingAccountFeedEntry>(newAccount);

if (createRequest.IsStatusValidForVerb)
{
    Console.WriteLine("Successfully created customer.");
}
else
{
    // There was a problem
    Console.WriteLine("Failed to create customer.");

}

Console.ReadKey(true);
Brian Mcnally
Posted: Wednesday, January 25, 2012 7:02:13 PM
Rank: Member

Joined: 1/1/2012
Posts: 15
Thanks Darron - very good example and really helpful, worked well.

I've tried converting it to a salesInvoiceFeedEntry version, creating just a simple Sales invoice against an account - but so far without success.

I get an "internal server" error reported by the webserver, with the explanation of "Object reference not set to an instance of an object".

I set (where invoice is the salesinvocie object)
invoice.CustomerId = "BRIAN"; //The customer I created with your example
invoice.reference = Console.ReadLine();
invoice.netTotal = 10;
invoice.lineCount = 0;
invoice.date = DateTime.Today;
invoice.taxDate = DateTime.Today;
invoice.grossTotal = 10;
invoice.taxTotal = 0;
invoice.type = "Product Invoice";
invoice.tradingAccountName = "BRIAN";
invoice.currency = "GBP";

I assumed initially it was because I wasn't providing all the mandatory fields, but this matches the data I get from a GET of a similar invoice from the same customer.

Do you have any suggestions or an invoice example I could look at?

Many thanks
Darron Cockram
Posted: Thursday, January 26, 2012 3:27:58 AM
Rank: Advanced Member

Joined: 10/13/2010
Posts: 117
Are you definitely running Accounts 2012? I only ask as I thought that generic error had been replaced with something more meningful.

The difference when creating an invoice is that it cannot be done in isolation like a tradingAccount. That is to say that a salesInvoice has dependencies on other resource kinds which must exist and we must include a reference to them. Specifically in the case of a salesInvoice it must contain a reference to the tradingAccount, that's the tradingAccount property which is something different from the CustomerId or tradingAccountName properties (which are actually extension properties).

So, if you add this to your code...

Code:
tradingAccount account = new tradingAccount();
tradingAccount.UUID = <UUID of previously created tradingAccount>;
invoice.tradingAccount = account;


...you should be able to create the invoice.

Couple of other things to note:
- You're not including any lines on the invoice (via the salesInvoiceLines colection property). As this breaks a business rule in Accounts in that an invoice record cannot exist without at least one line, a zero value 'message' line will be automatically created and associated with the invoice by the Sdata adapter. If you had included one or more salesInvoiceLine the message line would not be created.
- The lineCount, netTotal, taxTotal and grossTotal are automatically recalculated based on the salesInvoiceLines associated with the invoice. So the invoice that actually gets created will not match your input in this case e.g. the netTotal and grossTotal would be 0 and not 10. Again this is to ensure business rules are enforced.

Hope that helps.
Brian Mcnally
Posted: Saturday, January 28, 2012 10:41:23 AM
Rank: Member

Joined: 1/1/2012
Posts: 15
Yes using 2012.

I've given what you suggest ago and as you said, I now add invoices, but with a zero value due to the lack of lines!

When you say add a salesInvoiceLine, are these implemented as a list collection? I've created a salesInvoiceLineFeedentry, but these are not compatible with the required saleInvoiceLineFeed. I was expecting to find a .add method or similar. Where am I going wrong?

Thanks
Darron Cockram
Posted: Monday, January 30, 2012 3:36:04 AM
Rank: Advanced Member

Joined: 10/13/2010
Posts: 117
The lines are implemented as a Feed<T> (a collection of objects derived from FeedEntry). There is an Add method but it might not be immediately obvious. Try this:

Code:
salesInvoiceLineFeedEntry invoiceLine = new salesInvoiceLineFeedEntry();
invoiceLine.type = "Free Text";
invoiceLine.text = "An example line";
invoiceLine.quantity = 1;
invoiceLine.netTotal = 100;
invoiceLine.taxTotal = 20;
invoiceLine.grossTotal = 120;

invoice.salesInvoiceLines = new salesInvoiceLineFeed();
invoice.salesInvoiceLines.Entries.Add(freetextOrderLine);


That should create an S1 stock code line and the invoice totals should reflect the totals of the lines.

I suspect the next question might be how do you create a line using an existing product code. This is very similar to associating the tradingAccount with the salesInvoice:

Code:

invoiceLine.type = "Standard";
invoiceLine.commodity = new commodity();
invoiceLine.commodity.UUID = <UUID of an existing commodity>;
.....
Brian Mcnally
Posted: Sunday, February 05, 2012 4:42:45 PM
Rank: Member

Joined: 1/1/2012
Posts: 15
Thanks Darron - another great snippet that's saved my sanity!

So all invoice integration working a treat - then I went on to credit notes... Changing to use salesCreditFeedEntry etc. I get this error returned from the sdata service:

InternalServerError
ApplicationDiagnosis

POST operation failed - Invalid index specified for Item retrieval

at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData)
at Sage.Integration.Accounts50.SDOAdapter.ISDOFields.Item(Object& Index)


Have literally used the same example as above but changing to what I believe are the credit note equivalent. Is there another subtly?

Thanks
Darron Cockram
Posted: Monday, February 06, 2012 3:06:54 AM
Rank: Advanced Member

Joined: 10/13/2010
Posts: 117
Brian Mcnally wrote:
POST operation failed - Invalid index specified for Item retrieval


Funnily enough we came across the very same problem just a few days ago. Unfortunately it's caused by an issue in the Accounts Sdata adapter itself, so will require a patch. I'm not sure what the likely timescales would be for such a patch. If anyone from the Accounts team is reading the forum could you possibly comment please?
Brian Mcnally
Posted: Monday, February 06, 2012 12:12:38 PM
Rank: Member

Joined: 1/1/2012
Posts: 15
I did find you can post negative value invoices - but these appear in the invoice list in Accounts, but when you do an update - just vanish and don't appear on the sales ledger account.
Darron Cockram
Posted: Tuesday, February 07, 2012 3:09:24 AM
Rank: Advanced Member

Joined: 10/13/2010
Posts: 117
I had considered a negative invoice as a workaround but discounted it as an option as it breaks business rules in Accounts. Not sure why it would vanish from the list. To be honest I'm surprised the Sdata feeds allow you to post the negative invoice in the first place. Sounds like another one for the Accounts team to investigate.
Mr Martyn Kemp
Posted: Thursday, March 29, 2012 7:34:35 AM
Rank: Member

Joined: 3/26/2012
Posts: 2
I'm still struggling to extract the data out, I can get to the first namespace for feed but any other xmlnodelist I attempt gives me a null so i'm not referencing the namespace correctly and I have tried many variations.

Could you give an example of how to get the customer list.

Heres what i've slapped together so far to try and consume the Sdata service Any help will be appreciated!

Code:


namespace RestConsume
{
    class Program
    {
        static void Main(string[] args)
        {
            Uri uriCustomers = new Uri("http://appserver:5493/sdata/accounts50/SDO/%7B360D45F2-FFAC-44AB-9A2E-7B210A7B3B96%7D-1/Customers");
            XmlDocument xDoc = new XmlDocument();
                       
            NetworkCredential nc = new NetworkCredential("MANAGER", "");
            WebRequest webRequest = WebRequest.Create(uriCustomers) as HttpWebRequest;
            webRequest.Credentials = nc;
            webRequest.PreAuthenticate = true;

            StreamReader reader;
           
            //var myCustomers = new CustomerInformation();

            using (WebResponse webResponse = webRequest.GetResponse() as WebResponse)
            {
                // Pull response stream
                reader = new StreamReader(webResponse.GetResponseStream());
                xDoc.Load(reader); //Console.WriteLine(xDoc.OuterXml);
               
                // Loading up namespaces
                var names = new XmlNamespaceManager(xDoc.NameTable);
                names.AddNamespace("atom", "http://www.w3.org/2005/Atom");
                names.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema");
                names.AddNamespace("cf", "http://www.microsoft.com/schemas/rss/core/2005");
                names.AddNamespace("entity", "http://schemas.sage.com/accounts50/2008");
                names.AddNamespace("sdatasync", "http://schemas.sage.com/sdata/sync/2008/1");
                names.AddNamespace("sdata", "http://schemas.sage.com/sdata/2008/1");
                names.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
                names.AddNamespace("opensearch", "http://a9.com/-/spec/opensearch/1.1/");
                names.AddNamespace("sme", "http://schemas.sage.com/sdata/sme/2007");
                names.AddNamespace("http", "http://schemas.sage.com/sdata/http/2008/1");
                names.AddNamespace("default", "xmlns:accounts50='http://schemas.sage.com/accounts50/2008' xmlns:sdata='http://schemas.sage.com/sdata/2008/1' "); // think this is what the javascript example means


                XmlElement root = xDoc.DocumentElement;
                XmlNodeList nodes = root.SelectNodes("//atom:feed", names);
                //XmlNodeList nodes = root.SelectNodes("//atom:feed/entry/sdata:payload/entity:", names);

                Console.WriteLine(root == null ? "(no root)" : root.GetAttribute("value"));

                foreach (XmlNode node in nodes)
                {
                    Console.WriteLine(node["id"].InnerText);
                    //Console.WriteLine(node["entry/sdata:payload/entity:customer/entity:name"].InnerText);
                }

                // Changing namespace
                XmlNodeList nodesEntity = xDoc.GetElementsByTagName("entity:Customer");
                               
                foreach (XmlNode node in nodesEntity)
                {
                    Console.WriteLine(node["name"].InnerText);
                }


                //myData = XDocument.Load(reader);
                webResponse.Close();
                webResponse = null;

            }


            /// Clean up my damn mess
            reader.Close();
            reader.Dispose();
            reader = null;

            webRequest = null;

            // wait for me to debug in case i forget my breakpoint d'oh
            Console.ReadLine();
           
           
        }
}
Darron Cockram
Posted: Thursday, March 29, 2012 8:50:32 AM
Rank: Advanced Member

Joined: 10/13/2010
Posts: 117
Mr Martyn Kemp wrote:
I'm still struggling to extract the data out, I can get to the first namespace for feed but any other xmlnodelist I attempt gives me a null so i'm not referencing the namespace correctly and I have tried many variations.

Could you give an example of how to get the customer list.

Heres what i've slapped together so far to try and consume the Sdata service Any help will be appreciated!


Try something like this:

Code:

static void Main(string[] args)
        {
            // Create a new instance of an endpoint feed.
            tradingAccountFeed feed = new tradingAccountFeed();

            // Build the request URI. This example assumes that the Accounts 50 GCRM contract implementation is available
            SDataUri uri = new SDataUri();
            uri.BuildLocalPath("Accounts50", "GCRM", "-", "tradingAccounts");
            // There could potentially be a very large number of records so we just request up to the first 10
            uri.Count = 10;

            long processed = 0;

            do
            {
                uri.StartIndex = processed + 1;

                // Build an Sdata request from the URI
                SDataRequest request = new SDataRequest(uri.Uri);
                request.AllowPromptForCredentials = false;
                request.Username = "MANAGER";
                request.Password = string.Empty;

                // Submit the request for trading accounts
                request.RequestFeed<tradingAccountFeedEntry>(feed);

                if (request.IsStatusValidForVerb)
                {
                    // Ensure we actually got some results
                    if (feed.Entries == null || feed.Entries.Count == 0)
                    {
                        Console.WriteLine("No more records");
                        break;
                    }

                    // Display the feed data
                    Console.WriteLine("Retrieved records {0} to {1} of {2} trading accounts", processed + 1, feed.Entries.Count + processed, feed.TotalResults);
                    foreach (tradingAccountFeedEntry entry in feed.Entries)
                    {
                        Console.WriteLine(entry.name);
                        ++processed;
                    }
                }
                else
                {
                    // There was a problem
                    Console.WriteLine("Request failed. Response was {0}", request.HttpStatusCode.ToString());
                    if (request.Diagnosis != null)
                        Console.WriteLine(request.Diagnosis.Message);

                    break;
                }

                Console.WriteLine(string.Empty);
                Console.WriteLine("Continue? (Y/N):");
                bool stopProcessing = (Console.ReadKey(true).Key != ConsoleKey.Y);
                   
                // Check if we have processed all the records or the user has aborted
                if (stopProcessing || !feed.TotalResults.HasValue || processed >= feed.TotalResults.Value)
                    break;
            }
            while (true);

            Console.WriteLine(string.Empty);
            Console.WriteLine("Processing complete");
            Console.ReadKey(true);
        }
    }


NOTE: You will need to add a reference to the following assemblies:
C:\Program Files\Sage\Assemblies\SData\Sage.Common.Syndication.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Integration.Accounts50.SDO.Adapter.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Integration.Accounts50.SDO.Adapter.GCRM.Feeds.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Integration.Client.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Integration.Server.Model.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Utilities.dll

If you're on an x64 version of Windows amend those paths to C:\Program Files (x86)\....

The other thing to mention, and this also is a problem in the JavaScript sample, would be that you should really be using the GCRM contract rather than the the SDO contract. The SDO contract has never been officially supported and was discontinued with the Accounts 2012 release.
Mr Martyn Kemp
Posted: Thursday, March 29, 2012 10:10:01 AM
Rank: Member

Joined: 3/26/2012
Posts: 2
Thank you very much, works a treat and is far simpler. where can i find more documentation on the helper classes

C:\Program Files\Sage\Assemblies\SData\Sage.Common.Syndication.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Integration.Accounts50.SDO.Adapter.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Integration.Accounts50.SDO.Adapter.GCRM.Feeds.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Integration.Client.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Integration.Server.Model.dll
C:\Program Files\Sage\Assemblies\SData\Sage.Utilities.dll
Darron Cockram
Posted: Friday, March 30, 2012 3:23:00 AM
Rank: Advanced Member

Joined: 10/13/2010
Posts: 117
There isn't any documentation available just yet I'm afraid but our Developer Support Team are currently busy working on producing the necessary documentation, code samples, training courses, etc.

Officially 3rd party support for Sdata is still in beta at the moment until we are ready with these supporting materials. I should stress that the software itself is not beta and is fully tested and functional - it's 'just' the supporting materials that aren't ready yet.

In the meantime if you have any questions just post them here.
Users browsing this topic
Guest


Forum Jump
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.

Main Forum RSS : RSS