|
|
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.
|
|
Rank: Member
Joined: 2/4/2010 Posts: 4
|
Any help in SData for beginners?
|
|
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.
|
|
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.zipThe 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.
|
|
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(); }
|
|
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.
|
|
Rank: Member
Joined: 1/1/2012 Posts: 15
|
Has anyone got a POST example they could share? (Ideally in C# using GCRM! ;-)
|
|
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);
|
|
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
|
|
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.
|
|
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
|
|
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>; .....
|
|
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
|
|
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?
|
|
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.
|
|
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.
|
|
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(); } }
|
|
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.
|
|
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
|
|
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.
|
|
|
Guest |