How can I fetch an array of data from a custom GI using soap contract based web services - acumatica

I know how to fetch data from a custom Generic Inquiry using standard soap / page-based web services.
Here's my code for standard web services to get the results from a custom GI:
static void Main(string[] args)
{
GI000081.Screen context = new GI000081.Screen();
context.Url = "http://localhost/AcumaticaDB181000062/(W(6))/Soap/GI000081.asmx";
context.CookieContainer = new System.Net.CookieContainer();
LoginResult loginResult = context.Login("admin", "Passw0rd");
if (loginResult.Code != ErrorCode.OK)
{
throw new Exception(loginResult.Message);
}
GI000081.Content GI000081Content;
GI000081Content = context.GetSchema(); //.IN202500GetSchema();
//Here's the code to obtain the GI data:
string[][] GI000081Data = context.Export
(new Command[] {
GI000081Content.Result.AccountID,
GI000081Content.Result.Address,
GI000081Content.Result.CustomerID,
GI000081Content.Result.AccountName
},
null, //This is the filter - none here, so null..
0,
false,
false
);
}
My request is, can I get an example of C# code for how to do this using the Contract-based web services. I know how to extend the endpoint and get the wsdl file / service reference to my custom Generic Inquiry, but I don't know the syntax to make the actual call.
Thanks in advance...

Just to make sure that you create the entity in the endpoint properly, make sure that the top level entity contain only the Parameters and that it has a sub entity of type details contain all the results. If there is no parameter then it is fine for the top level entity to be empty.
Here is the code sample that I used
class Program
{
static void Main(string[] args)
{
DefaultSoapClient client = new DefaultSoapClient();
client.Login("admin", "admin", null, null, null);
try
{
BatchPaymentsInq batch = new BatchPaymentsInq
{
Result = new BatchPaymentsInqResult[]
{
new BatchPaymentsInqResult { ReturnBehavior = ReturnBehavior.All }
}
};
var result = client.Get(batch);
}
catch(Exception ex)
{
}
finally
{
client.Logout();
}
}
}
Edit:
Here is how I extended my endpoint in order to use it with the Contract Based SOAP API
So the main entity named BatchPaymentsInq is pointing to the Generic Inquiry screen and will not have any field in it as you have mentioned that there is no parameter.
The sub entity Result is an array of BatchPaymentsInqResult an object created for containing the fields in the result grid of the inquiry.

Related

Release invoice on new screen

I need your help.
I have created a new screen, where I am calling all invoices pending release.
I have problems to release, I send a message where you request (you want to release).
It shows me the infinite message.
Only once should you ask me, then you should go out and follow the normal process.
public ProcessDocNew()
{
// Acuminator disable once PX1008 LongOperationDelegateSynchronousExecution [Justification]
Document.SetProcessDelegate(
delegate (List<ARInvoice> list)
{
List<ARRegister> newlist = new List<ARRegister>(list.Count);
foreach (ARInvoice doc in list)
{
newlist.Add(doc);
}
ProcessDoc(newlist, true);
}
);
Document.SetProcessCaption(ActionsMensje.Process);
Document.SetProcessAllCaption(ActionsMensje.ProcessAll);
}
public virtual void ProcessDoc(List<ARRegister> list, bool isMassProcess)
{
string title = "Test";
string sms = "¿Stamp?";
var Graph = PXGraph.CreateInstance<ARInvoiceEntry>();
ARInvoice document = Document.Current;
PEFEStampDocument timbrar = new PEFEStampDocument();/*This is a class where it is, all my method*/
if (isMassProcess == true)
{
Document.Ask(title, sms, MessageButtons.YesNo, MessageIcon.Question);
{
PXLongOperation.StartOperation(Graph, delegate
{
timbrar.Stamp(document, Graph); /*here I have my release method*/
});
}
}
}
public static class ActionsMensje
{
public const string Process = "Process";
public const string ProcessAll = "Process All";
}
I await your comments
Only once should you ask me, then you should go out and follow the
normal process.
That is not how the processing pattern works. The process delegate is called for each record and is therefore not a valid location to display a message that should be shown only once.
You would need to add a custom action to achieve that behavior. The scenario you're looking for should be implemented with a processing filter checkbox and processing filter to comply with best practices:
Documentation on processing screens implementation is available here:
https://help-2019r2.acumatica.com/Help?ScreenId=ShowWiki&pageid=a007b57b-af69-4c0f-9fd1-f5d98351035f

How to make outgoing request or webhook in Acumatica?

I'm integrating an Asp.NET application with Acumatica that needs to update shipping information (tracking #, carrier, etc.) when it becomes available in Acumatica. Is there a way to have Acumatica call an endpoint on my Asp.NET app when a shipment is created? I've searched through a lot of the docs (available here), but I haven't come across anything to send OUT information from Acumatica to another web service.
Ideally, this outgoing call would send the shipment object in the payload.
This wasn't available when you asked the question but push notifications seem to be exactly what you're looking for:
Help - https://help.acumatica.com/(W(9))/Main?ScreenId=ShowWiki&pageid=d8d2835f-5450-4b83-852e-dbadd76a5af8
Presentation - https://adn.acumatica.com/content/uploads/2018/05/Push-Notifications.pdf
In my answer I suppose that you know how to call some outside service from C# code, and for your is a challenge how to send notification from Acumatica.
I propose you to extend each Persist method in each Acumatica graph, from which you expect to send notification when object is persisted in db. IMHO the best option for this is to override method persist ( btw, it overriding persist method is well described in T300 ). In code of extension class you can do the following:
public void Persist(PersistDelegate baseMethod)
{
baseMethod(); // calling this method will preserve your changes in db
//here should go your code, that will send push/pop/delete etc web request into your asp.net application. Or in other words your web hook.
}
If you don't have Acumatica 2017R2, then you have to create your own extension project and then you can call it from your Acumatica code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
namespace MyApp
{
public static class Utility
{
private static WebRequest CreateRequest(string url, Dictionary headers)
{
if (Uri.IsWellFormedUriString(url, UriKind.Absolute))
{
WebRequest req = WebRequest.Create(url);
if (headers != null)
{
foreach (var header in headers)
{
if (!WebHeaderCollection.IsRestricted(header.Key))
{
req.Headers.Add(header.Key, header.Value);
}
}
}
return req;
}
else
{
throw(new ArgumentException("Invalid URL provided.", "url"));
}
}
public static string MakeRequest(string url, Dictionary headers = null)
{
WebResponse resp = CreateRequest(url, headers).GetResponse();
StreamReader reader = new StreamReader(resp.GetResponseStream());
string response = reader.ReadToEnd();
reader.Close();
resp.Close();
return response;
}
public static byte[] MakeRequestInBytes(string url, Dictionary headers = null)
{
byte[] rb = null;
WebResponse resp = CreateRequest(url, headers).GetResponse();
using (BinaryReader br = new BinaryReader(resp.GetResponseStream()))
{
rb = br.ReadBytes((int)resp.ContentLength);
br.Close();
}
resp.Close();
return rb;
}
}
}
You can then call it like this:
try
{
Utility.MakeRequest(theUrl, anyHeadersYouNeed);
}
catch(System.Net.WebException ex)
{
throw(new PXException("There was an error.", ex));
}

How to insert multiple objects in to Azure Mobile Services table controller [.Net backend]

I have an Azure Mobile service coded in .net Web API. I have a TableController. I want that table controller to be able to insert multiple persons, not just one person with from the client with InsertAsync(myPerson). I have the following code in the TableController:
[RequiresAuthorization(AuthorizationLevel.Admin)]
public async Task<bool> InsertPersons(List<Person> values)
{
try
{
foreach (var item in values)
{
var current = await InsertAsync(item);
}
return true;
}
catch (System.Exception)
{
return false;
}
}
The problem is in the client. Because it is strongly typed it only allows me to insert one item at a time. How must I call the server from the client? Do I have to write a Custom Api Controller and call it with mobileService.InvokeApiAsync? If so, how can I get access to my database from a Custom API Controller that doesn't inherit from TableController?
Thank you so much!
The helper methods in the TableController<T> base class assume that the insert operations apply to a single object - and the InsertAsync method in the client also assumes the same. So even though you can define in a table controller a method that takes an array (or list) of Person, you won't be able to call it via the client SDK (at least not without some heavy-lifting using a handler, for example).
You can, however, create a custom API which takes such a list. And to insert the multiple items from the API, you can access the context directly, without needing to go through the helper methods from the table:
public class PersonController : ApiController
{
test20140807Context context;
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
this.context = new test20140807Context();
}
[HttpPost]
public async Task<bool> InsertPersons(List<Person> values)
{
foreach (var value in values)
{
if (string.IsNullOrEmpty(value.Id))
{
value.Id = Guid.NewGuid().ToString();
}
}
try
{
this.context.People.AddRange(values);
await this.context.SaveChangesAsync();
return true;
}
catch (System.Exception ex)
{
Trace.WriteLine("Error: " + ex);
return false;
}
}
}
And on the client:
private async void btnTest_Click(object sender, RoutedEventArgs e)
{
var items = new Person[]
{
new Person { Name = "John Doe", Age = 33 },
new Person { Name = "Jane Roe", Age = 32 }
};
try
{
var response = await App.MobileService.InvokeApiAsync<Person[], bool>("person", items);
Debug.WriteLine("response: " + response);
}
catch (Exception ex)
{
var str = ex.ToString();
Debug.WriteLine(str);
}
}
From Carlos Figueira's post on inserting multiple items at once in azure mobile services, it looks like what you need to do is create another table called AllPersons. In your client, the AllPersons object would have a Persons array member. In your server side script for the AllPersons insert, you iterate through the AllPersons.Persons and insert into the table one by one.

cannot creat a domain using Rackspace Cloud DNS with openstack.net

I'm currently trying to create a domain on Rackspace Cloud DNS service using openstack.net sdk, but nothings happening.
i got my rackspace account, its activated and i got my API key as well.
I wrote a console app to test my code but the results are always "WaitingForActivation" and i cant find any documentation for cloudsDNS using openstack.net sdk.
Would anyone be able to take a look at my code and tell me what im doing wrong please.
private static string RackSpaceUserName
{
get { return "username"; }
}
private static string RackSpaceUserApiKey
{
get { return "apikey"; }
}
private UserAccess RackSpaceUser
{
get
{
IIdentityProvider identityProvider = new CloudIdentityProvider();
var userAccess = identityProvider.Authenticate(Cloudidentity);
return userAccess;
}
}
private static CloudIdentity Cloudidentity
{
get
{
var ci = new CloudIdentity
{
APIKey = RackSpaceUserApiKey,
Username = RackSpaceUserName
};
return ci;
}
}
static void Main(string[] args)
{
var ParkedDomain = new CloudDnsProvider(Cloudidentity, null, true, null);
List<DnsDomainRecordConfiguration> dnsrecords = new List<DnsDomainRecordConfiguration>();
dnsrecords.Add(new DnsDomainRecordConfiguration(DnsRecordType.Ns, "testdomain.com", "dns1.stabletransit.com", TimeSpan.FromMinutes(20), null, null));
dnsrecords.Add(new DnsDomainRecordConfiguration(DnsRecordType.Ns, "testdomain.com", "dns2.stabletransit.com", TimeSpan.FromMinutes(20), null, null));
List<DnsSubdomainConfiguration> subdomains = new List<DnsSubdomainConfiguration>();
subdomains.Add(new DnsSubdomainConfiguration("info#testdomain.com", "test", ""));
DnsDomainConfiguration dnsd = new DnsDomainConfiguration("testdomain.com", TimeSpan.FromMinutes(20), "info#testdomain.com", "", dnsrecords, subdomains);
DnsConfiguration dnsconfig = new DnsConfiguration(dnsd);
var result = ParkedDomain.CreateDomainsAsync(dnsconfig, AsyncCompletionOption.RequestCompleted, CancellationToken.None, null);
Console.Write(result.Status);
Console.ReadLine();
}
the value for result.Status always returns "WaitingForActivation", and when i check my rackspace cloud dns dashboard, no domains are created.
Any help would be greatly appreciated.
The DNS service uses asynchronous methods. The object returned by CreateDomainsAsync is a Task which represents the asynchronous operation, but that operation may not complete prior to the call returning. You can wait for the request to be complete in your code by any of the following methods:
Await the result (only allowed in an async method, which Main cannot be).
await result;
Access the Task<TResult>.Result property.
var completedResult = result.Result;
Call the Task.Wait method.
result.Wait();

PostEntityImage in update message post-operartion has no key at all

I have registered a plugin for update message of some entity (in post-operation) via CRM tool in Visual Studio and also registered post-image for that plugin like below:
and here is my code :
protected void ExecutePostOpportunityUpdate(LocalPluginContext
localContext)
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}
// TODO: Implement your custom Plug-in business logic.
IPluginExecutionContext context = localContext.PluginExecutionContext;
Entity postImage = (Entity)context.PostEntityImages["PostImage"];
....
}
But it throws an error and says that there is no key in PostEntityImages at all. I debugged the plugin and saw that there is no key in that at all.
Would you help me please?
Well looking at your code you have to retrieve the actual entity of Opportunity: Try this
try
{
Entity postOpportunityService = (Entity)context.PostEntityImages["PostImage"];
// Opportunity service's parent opportunity lookup reference
EntityReference opportunityReference = (EntityReference)postOpportunityService.Attributes["mpc_opportunityid"];
// Columns to be retrieved for opportunity (aka. columns to be edited)
ColumnSet opportunityColumnSet = new ColumnSet(new string[] { "estimatedvalue", "mpc_estoneoffinvoicing", "mpc_estinvoicingperyear" });
// Retrieve actual opportunity entity
Entity opportunity = service.Retrieve(opportunityReference.LogicalName, opportunityReference.Id, opportunityColumnSet);
}
catch (FaultException<OrganizationServiceFault> ex) { tracingService.Trace("FaultException", ex.ToString()); }

Resources