Jump to content

Stuart Eastland

Members
  • Posts

    19
  • Joined

  • Last visited

  • Days Won

    1

Everything posted by Stuart Eastland

  1. OK, so I just tried it without specifying ANY attributes. The API documentation states that this will only return the "base attributes (name, path)" however it has returned the full data set including nested data such as contact. Poorly written documentation or am I missing something?
  2. This is all I get: {{ "id": "8057948faa504a7ea77f6399c7ae0408", "displayed_as": "SI-1008542", "$path": "/sales_invoices/8057948faa504a7ea77f6399c7ae0408", "date": "2022-11-30", "reference": "DN 19676", "shipping_net_amount": "51.0", "net_amount": "3242.72", "tax_amount": "12.83", "total_paid": "0.0", "last_paid": "" }} I am using the 32 character guid to retrieve the data. I need to get data for pro-forma which don't have a numeric id assigned.
  3. I am using the endpoint /sales_invoices/{key} to retrieve a sales invoice. I also specify the following parameters: "attributes" = "reference,vendor_reference,date,last_paid,net_amount,shipping_net_amount,tax_amount,total_paid,contact_id" "nested_attributes" = "contact" The API documentation suggests that I should be able to retrieve the nested data for contact, however the returned contact is null. The API documentation showing the sample payload also suggests that the contact_id might be returned as a field in the main body, hence I have added this to my attribute list, however this is also returned as null. The other data is returned as expected. Any suggestions on how to retrieve the contact key for a sales invoice and sales credit note?
  4. I've just gone through the process of getting the oAuth callback to work and have posted simplistic demo code in an earlier thread: Oauth2 Authorisation and IIS - Support - Sage Developer Community You need to have logic in the callback to retrieve the code sent from sage and then request the tokens. I found this easiest to do on a development machine using localhost. If you don't use localhost you need to use https. One thing to note is the token is refreshed without using the callback url, so it may be something that you only need to do initially as long as your refresh token does not expire (30 days?).
  5. Thanks Ben, that works perfectly although it does mean a second request when creating new products/contacts. Also your pointers to the "key concepts" in your help pages should reduce the number of further questions from me 😁 My project to allow our in-house system to push products, contacts and sales invoices directly into Sage went live this week following our migration from sage50 and should result in significant time (and error) savings. However, the json in your previous answer makes me question the best way for me to deal with paginated queries. I understand the principle behind the $total, $page, $items elements, however I have not found an efficient way to de-serialize the "$" prefixed data. I appreciate that this is more of a basic coding question rather than Sage specific, but I wondered if you can give me some hints for use with C#.
  6. I am able to push sales invoices into Sage using the API, however some of these need to be PROFORMA. When creating a new invoice online I can set the dropdown to DRAFT or PROFORMA but I cannot see any such option in the API.
  7. Here is my commented pseudo-code for the callback.Please feel free to peer-review and make suggestions for improvement. public partial class SageCallback : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (Request.Params["code"] == null) { Response.Redirect("Default.aspx?Message=Missing [code] parameter in sage callback request."); } RestClient client = new RestClient("https://oauth.accounting.sage.com/token"); RestRequest request = new RestRequest(Method.POST); request.AddObject(new { code = Request.Params["code"], client_id = < your SageClientId >, client_secret = < your SageClientSecret >, redirect_uri = < your SageCallbackUrl >, grant_type = "authorization_code" }); IRestResponse response = client.Execute(request); if (response.ErrorException != null) { Response.Redirect("Default.aspx?Message=" + response.ErrorException.Message); } if (response.StatusCode.ToString() != "OK") { Response.Redirect("Default.aspx?Message=Unexpected error authenticating Sage"); } User currentUser = < persisted user object >; currentUser.SageAccessToken = GetJsonValue(response.Content, "access_token"); currentUser.SageRefreshToken = GetJsonValue(response.Content, "refresh_token"); long expires = long.Parse(GetJsonValue(response.Content, "expires_in")); // seconds currentUser.SageAccessTokenExpiry = DateTime.UtcNow.AddSeconds(expires); long refreshExpires = long.Parse(GetJsonValue(response.Content, "refresh_token_expires_in")); // seconds currentUser.SageRefreshTokenExpiry = DateTime.UtcNow.AddSeconds(refreshExpires); currentUser.Save(); string url = "Default.aspx?Message=Sage access granted. Please retry Sage update request."; Response.Redirect(url); } }
  8. Here is my commented pseudo-code for getting the token. I'll post the code for the callback separately. Please feel free to peer-review and make suggestions for improvement. private static string GetOAuthToken() { User currentUser = new <persisted user data >; // if the token has not expired, use it if (currentUser.SageAccessTokenExpiry != DateTime.MinValue && currentUser.SageAccessTokenExpiry > DateTime.UtcNow) { return currentUser.SageAccessToken; } // if refresh token has not expired, get a new token if (currentUser.SageRefreshTokenExpiry != DateTime.MinValue && currentUser.SageRefreshTokenExpiry > DateTime.UtcNow) { RestClient client = new RestClient("https://oauth.accounting.sage.com/token"); RestRequest request = new RestRequest(Method.POST); request.AddObject(new { client_id = < your SageClientId >, client_secret = < your SageClientSecret >, refresh_token = currentUser.SageRefreshToken, redirect_uri = < your SageCallbackUrl >, grant_type = "refresh_token" }); IRestResponse response = client.Execute(request); if (response.ErrorException != null) { throw response.ErrorException; } if (response.StatusCode.ToString() != "OK") { if (!response.Content.Contains("refresh_token is invalid")) { throw new ApplicationException(response.Content); } } else { currentUser.SageAccessToken = Common.GetJsonValue(response.Content, "access_token"); currentUser.SageRefreshToken = Common.GetJsonValue(response.Content, "refresh_token"); long expires = long.Parse(Common.GetJsonValue(response.Content, "expires_in")); // seconds currentUser.SageAccessTokenExpiry = DateTime.UtcNow.AddSeconds(expires); long refreshExpires = long.Parse(Common.GetJsonValue(response.Content, "refresh_token_expires_in")); // seconds currentUser.SageRefreshTokenExpiry = DateTime.UtcNow.AddSeconds(refreshExpires); currentUser.Save(); return currentUser.SageAccessToken; } } // otherwise, start the process by requesting a new code HttpContext.Current.Session["SageRequestUrl"] = HttpContext.Current.Request.Url; string url = string.Format("https://www.sageone.com/oauth2/auth/central?filter=apiv3.1&response_type=code&client_id={0}&redirect_uri={1}&scope=full_access", Common.SageClientId, Common.SageCallbackUrl); HttpContext.Current.Response.Redirect(url); return string.Empty; }
  9. Hi Ben, I wonder if you can help me again? Your suggestion to use the links/href in the contact json resolved my requirements for contacts, however the products uses the same dual unique key scenario where the API requires the 32 character key and the pages require a numeric id which I cannot find anywhere in the json returned or a product. This does not have a links section. So, how do I find this numeric id for a product so I can construct a link from our internal website?
  10. In answer to my own question, I clearly need to use an SSL secured url that can be resolved externally so I need to get an SSL certificate installed on the server as my next step.
  11. I have been working on using the API running on a development machine with our website and oAuth callback on localhost. Just installed on our server which is running inside a fire wall and now get www.sageone.com refused to connect when trying to fetch the oauth token. I am using an internal ip address. Is this going to be possible? I figured that localhost was also local so a local ip address should work too.
  12. I started trying to do this in December. There is no API for sage50. We just migrated to Sage Online, far superior and excellent API. Task completed in a couple of days including full integration or products, customers and suppliers.
  13. Thanks. That is perfect. I thought that I had looked through the returned json but maybe missed this. I think the issue with the reference number is that the migration populated the reference number with the original invoice number and the invoice number with a transaction number that previously had not been visible to end users. This means that all our sales invoices now have different numbers to those sent to our customers. I anticipate some frustration when we start getting queries on these! I have not been very impressed by the migration. I don't see why the data above has been transposed as it is clear that I can create new invoices the way I want to, and use the reference number for our internal despatch number and continue to use the previous invoice number sequence in invoice number. Also the migrated data is read only. What happens if we need to change a journal code or reprint a sales order (print is disabled too).
  14. We push sales invoices from our in-house system into sage using the API, a process that also creates customers and suppliers in Sage if required. When I create a Contact (customer or vendor) using the API I get a 32 character "id" and the $path shows as /contacts/abcde where abcde is the 32 character id. When I use Sage online and search for a contact the resulting url looks like /contacts/customers/12345678 where 12345678 is an 8 digit numeric id which I cannot find in the data returned from Sage when I do a Contact GET. I note that the API uses the 32 character id when creating sales invoices, so this is what I am persisting against customers in our in-house system.. So my question is, how can I construct a url to a contact using the 32 character id? If this is not possible, how can I obtain the numeric id that Sage uses in the page url for contacts? As a wider question, why does Sage use so many different identifiers? It is even worse in a sales invoice which has the 32 character id, a SI-nnnnn transaction id and another sequential sales invoice id in the reference field. the SI-nnnnn seems unnecessary. We have just migrated from sage50 where we used to use the reference column to hold a unique identifier to the corresponding despatch data in our own system, but there is no suitable place for this now.
  15. I am using POST to create a new sales order but I get an error saying that "contact cannot be found". I am using a unique reference for all my contacts which matches the database id from our in-house database as a lookup on contacts (customer/supplier), however I am supplying the numeric "id" for a customer which I get from the customer details page url when using the api.
  16. I was not building my request correctly and I can now get the token back using my c# code. I found the sample code rather difficult to follow as I am an old school ASP Forms developer (no MVC or SPA) so I stripped it right down the the basic Request/Response mechanism and cut it down significantly.
  17. I have noticed that the code that is returned (and the one in the sample code from Authentication | Sage Developer | Accounting) starts with "GB/" which looks like to country code. The sample request for a token has stripped this off. Is this correct? Response: GET https://myapp.com/auth/callback?code=GB%2F12abcxxxxxxxxxxxxf7d&country=GB&state=random_string Request: POST https://oauth.accounting.sage.com/token Content-Type: application/x-www-form-urlencoded client_id=4b6xxxxxxx710 &client_secret=iNuxxxxxxxxxxtm9 &code=12axxxxxxxxxxxxf7d &grant_type=authorization_code &redirect_uri=https://myapp.com/auth/callback
  18. I found that my initial problem was the value I was supplying for scope was incorrect. I am now able to make the initial request and get a code returned, however when I send this back for a token I am getting a http 400 error. I'll compare my request string with the postman values...
  19. I am a new Sage developer and have reached the same point. Postman generates tokens which can then be used to generate successful API requests but my C# code (even using the SageOne API Sample) fails with Authorise Application Error. Any pointers, especially other examples of the OAuth logic using ASP.NET Framework WebPages C# rather than .NET Core, would be appreciated.
×
×
  • Create New...