Acumatica Rest API: Using .ActionConvertToBAccount in Postman sending parameters - acumatica

I'm trying to create a Business Account from Leads (ScreenID: CR301000) via Acumatica (Version: 17.200.0401) REST API in Postman with the action (Create Business Account) in Acumatica, called ConvertLeadToBAccount in the endpoint with the parameters: AccountClass, AccountName, BAccountID.
I tried the following:
{
"entity": {
"LeadID": {
"value": "100005"
}
},
"parameters": {
"AccountClass": {
"value": "BUSINESS"
},
"AccountName": {
"value": "MACRO"
},
"BAccountID": {
"value": "GIMON1"
}
}
}
However, it's not converting the lead into a business account.
This is the Post Request:
Post Request in Postman
The LeadID is previously obtained via this Get Request:
Get Request via Postman
Unlike other actions we have executed, this one uses parameters, but we cannot find examples with the use of parameters.

This was a know issue that was reported to the engineering team of Acumatica and a fix was release in these versions:
for the 2017 R2 version: 17.202.0016
for the 6.1 version: 6.10.1417
If upgrading to one of the aforementioned builds is not an option, please consider creating the following customization for the LeadMaintExt BLC to override the standard ConvertToBAccount action:
using PX.Data;
using System.Collections;
using System.Linq;
namespace PX.Objects.CR
{
public class LeadMaintExt : PXGraphExtension< LeadMaintExt >
{
public PXAction<Contact> convertToBAccount;
[PXUIField(DisplayName = Messages.ConvertToBAccount,
MapEnableRights = PXCacheRights.Update,
MapViewRights = PXCacheRights.Select)]
[PXButton(ImageKey = PX.Web.UI.Sprite.Main.Process)]
public virtual IEnumerable ConvertToBAccount(PXAdapter adapter)
{
if (Base.IsContractBasedAPI)
{
Base.AccountInfo.View.SetAnswer(null, WebDialogResult.Cancel);
var contacts = Base.convertToBAccount.Press(adapter)
.RowCast<Contact>().ToList();
foreach (Contact l in contacts)
{
Base.Save.Press();
Contact lead = l;
lead.ConvertedBy = Base.Accessinfo.UserID;
PXLongOperation.StartOperation(Base, () =>
{
try
{
LeadMaint.ConvertToAccount(lead,
Base.AccountInfo.Current);
}
catch (PXRedirectRequiredException ex)
{
var accountMaint = ex.Graph;
accountMaint.Actions.PressSave();
}
});
}
return contacts;
}
else
{
return Base.convertToBAccount.Press(adapter);
}
}
}
}

Related

Azure Function: How to add row to cloud table on blob creating?

I'm developing an Azure Function which should add new line to an Azure table when new a new blob is added. The application has many containers in Blob Storage, and my Azure Function should process all blobs from all containers.
I tried to implement event getting with EventGrid, but it gives an error.
My Azure function:
#r "D:\home\site\wwwroot\BlobCreatedFunction\Microsoft.Azure.EventGrid.dll"
#r"D:\home\site\wwwroot\BlobCreatedFunction\Microsoft.WindowsAzure.Storage.dll"
using Microsoft.Azure.EventGrid.Models;
using Microsoft.WindowsAzure.Storage.Table;
using System;
public class TemporaryBlobEntity : TableEntity
{
public TemporaryBlobEntity(string partitionKey, string rowKey)
{
this.PartitionKey = partitionKey;
this.RowKey = rowKey;
}
public string BlobUrl { get; set; }
public DateTime BlobUploaded { get; set; }
}
public static TemporaryBlobEntity Run(EventGridEvent eventGridEvent, ILogger log)
{
if (eventGridEvent.Data is StorageBlobCreatedEventData eventData)
{
log.LogInformation(eventData.Url);
log.LogInformation(eventGridEvent.Data.ToString());
var temporaryBlob = new TemporaryBlobEntity("blobs", eventData.Url)
{
BlobUrl = eventData.Url,
BlobUploaded = DateTime.UtcNow
};
return temporaryBlob;
}
return null;
}
Here is my integration JSON:
{
"bindings": [
{
"type": "eventGridTrigger",
"name": "eventGridEvent",
"direction": "in"
},
{
"type": "table",
"name": "$return",
"tableName": "temporaryBlobs",
"connection": "AzureWebJobsStorage",
"direction": "out"
}
]
}
In my Azure Function settings, I added the value for AzureWebJobsStorage.
When I press Run in the test section, logs show:
2019-07-08T13:52:16.756 [Information] Executed 'Functions.BlobCreatedFunction' (Succeeded, Id=6012daf1-9b98-4892-9560-932d05857c3e)
Looks good, but there is no new item in cloud table. Why?
Then I tried to connect my function with EventGrid topic. I filled new subscription form, selected "Web Hook" as endpoint type, and set subscriber endpoint at: https://<azure-function-service>.azurewebsites.net/runtime/webhooks/EventGrid?functionName=<my-function-name>. Then I got the following error message:
Deployment has failed with the following error: {"code":"Url validation","message":"The attempt to validate the provided endpoint https://####.azurewebsites.net/runtime/webhooks/EventGrid failed. For more details, visit https://aka.ms/esvalidation."}
As far as I can understand, the application needs some kind of request validation. Do I really need to implement validation in each of my azure functions? Or shoudl I use another endpoint type?
When you enter a webhook into Event Grid it sends out a request to verify that you actually have permissions on that endpoint. The easiest way to connect a Function to Event Grid is to create the subscription from the Functions app instead of the Event Grid blade.
Opening up the Function in the portal you should find a link at the top to "Add Event Grid subscription". Even if the Functions app was created locally and published to Azure so the code isn't viewable the link will be available.
This will open up the screen for creating an Event Grid subscription. The difference is that instead of the Event Grid topic info being prefilled, the web hook info is prepopulated for you. Fill in the info about the Event Grid topic to finish creating the subscription.
If you decide you want to implement the validation response for whatever reason, it is possible to do this by checking the type of the message.
// Validate whether EventType is of "Microsoft.EventGrid.SubscriptionValidationEvent"
if (eventGridEvent.EventType == "Microsoft.EventGrid.SubscriptionValidationEvent")
{
var eventData = (SubscriptionValidationEventData)eventGridEvent.Data;
// Do any additional validation (as required) such as validating that the Azure resource ID of the topic matches
// the expected topic and then return back the below response
var responseData = new SubscriptionValidationResponse()
{
ValidationResponse = eventData.ValidationCode
};
if (responseData.ValidationResponse != null)
{
return Ok(responseData);
}
}
else
{
//Your code here
}
There is also an option to validate the link manually by getting the validation link out of the validation message and navigating to it in your browser. This method is primarily for 3rd party services where you can't add the validation code.
The following are changes in your EventGridTrigger function:
#r "Microsoft.WindowsAzure.Storage"
#r "Microsoft.Azure.EventGrid"
#r "Newtonsoft.Json"
using System;
using Newtonsoft.Json.Linq;
using Microsoft.Azure.EventGrid.Models;
using Microsoft.WindowsAzure.Storage.Table;
public static TemporaryBlobEntity Run(EventGridEvent eventGridEvent, ILogger log)
{
log.LogInformation(eventGridEvent.Data.ToString());
var eventData = (eventGridEvent.Data as JObject)?.ToObject<StorageBlobCreatedEventData>();
if(eventData?.Api == "PutBlob")
{
log.LogInformation(eventData.Url);
return new TemporaryBlobEntity("blobs", eventData.Sequencer)
{
BlobUrl = eventData.Url,
BlobUploaded = DateTime.UtcNow
};
}
return null;
}
public class TemporaryBlobEntity : TableEntity
{
public TemporaryBlobEntity(string partitionKey, string rowKey)
{
this.PartitionKey = partitionKey;
this.RowKey = rowKey;
}
public string BlobUrl { get; set; }
public DateTime BlobUploaded { get; set; }
}
Notes:
You don't need to validate an EventGridTrigger function for AEG subscription webhook endpoint. This validation is built-in the preprocessing of the EventGridTrigger function.
The eventGridEvent.Data property is a JObject and must be converted (deserialized) to the StorageBlobCreatedEventData object, see here.
For RowKey (and PartitionKey) see the restriction characters in here, so I changed it to the Sequencer value in this example.
The AEG subscription webhook endpoint for the EventGridTrigger function has the following format:
https://{azure-function-service}.azurewebsites.net/runtime/webhooks/EventGrid?functionName={my-function-name}&code={systemkey}

TelemetryClient request tracking end to end with trace

It is possible to trace a request along with any traces that I have added via the TelemetryClient. In a nutshell, I am using:
var id = "a very unique id";
using (telemetryClient.StartOperation<RequestTelemetry>("Name", id, id))
{
telemetryClient.Trace("I have done something", new Dictionary<string, string> { { "uniqueId", id } );
telemetryClient.Trace("I am doing something else", new Dictionary<string, string> { { "uniqueId", id } );
}
The problem here is that the operationId isn't set nor the operationParentId.
Perhaps I am using this incorrectly but I was hoping to do a join between traces and requests on operationParentId so I can get the full picture.
I suggest you use the latest version of Microsoft.ApplicationInsights 2.10.0
With the latest nuget package installed, I test the code you provide, operationId / operationParentId are set properly.
Code:
static void Main(string[] args)
{
TelemetryClient client = new TelemetryClient() { InstrumentationKey = "xxxx" };
string id = "b123456";
using (client.StartOperation<RequestTelemetry>("op_name",id,id))
{
client.TrackTrace("I have done something", new Dictionary<string, string> { { "my_uniqueId", id } } );
client.TrackTrace("I am doing something else", new Dictionary<string, string> { { "my_uniqueId", id } } );
}
Console.ReadLine();
}
In portal:
Request telemetry:
Trace telemetry:

Getting error UserSession.OnActionExecuting(ActionExecutingContext): no suitable method found to override in mvc 5?

I have created one demo in mvc 5 and now I need to create one custom filter in my demo. I have used mvc 5.
I need to check every time what method is execute like is a ajax call or action method call in mvc.
Here I have write like this code in my class.
public class UserSession
: System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var session = filterContext.HttpContext.Session;
if (ApplicationSession.IsSessionAlive)
return;
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
var ajaxRedirectTarget = new RouteValueDictionary { { "action", "FailAuthenticationAjax" }, { "controller", "Home" } };
filterContext.Result = new RedirectToRouteResult(ajaxRedirectTarget);
}
else
{
var redirectTarget = new RouteValueDictionary { { "action", "Login" }, { "controller", "Account" } };
filterContext.Result = new RedirectToRouteResult(redirectTarget);
}
}
}
but I got error like this UserSession.OnActionExecuting(ActionExecutingContext): no suitable method found to override
After I have put this class on my controller like this.
[UserSession]
public class DashboardController
{
}
any one know how to fixed this issue in mvc 5?

Deployed Aspnet WebApi to IIS OK but return 500 internal Server Error When CRUD

I am just starting asp.net Core 2.0 web api. Please extend your help.
I have successfully deployed the asp.net WebApi app to Local IIS and
I have created a database in Sql Server 2017 (free version) with below code.
problem: status 500 internal server error when I do a post with PostMan
1) in StartUp.cs with ConnectionString:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<ContactContext>(option => option.UseSqlServer(Configuration.GetConnectionString("Default")));
}
2) connection string in appsettings.json
"connectionstrings": {
"Default": "Server = .\\SQL2017Express; Database = ContactDB; Integrated Security = True;"
}
**The Problems Encountered:**
problem: status 500 internal server error when I do a post with PostMan
Post : http://192.168.1.8:8989/api/contacts
Json Data for the Body in PostMan:
{
"FirstName" : "John",
"LastName" : "David",
"MobilePhone" : "123456789"
}
// controller
[Produces("application/json")]
[Route("api/[controller]")]
public class ContactsController : Controller
{
public IContactRepository ContactsRepo { get; set; }
public ContactsController(IContactRepository _repo)
{
ContactsRepo = _repo;
}
[HttpGet]
public async Task<IActionResult> GetAll()
{
var contactList = await ContactsRepo.GetAll();
return Ok(contactList);
}
[HttpGet("{id}", Name = "GetContacts")]
public async Task<IActionResult> GetById(string id)
{
var item = await ContactsRepo.Find(id);
if (item == null)
{
return NotFound();
}
return Ok(item);
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] Contacts item)
{
if (item == null)
{
return BadRequest();
}
await ContactsRepo.Add(item);
return CreatedAtRoute("GetContacts", new { Controller = "Contacts", id = item.MobilePhone }, item);
}
[HttpPut("{id}")]
public async Task<IActionResult> Update(string id, [FromBody] Contacts item)
{
if (item == null)
{
return BadRequest();
}
var contactObj = await ContactsRepo.Find(id);
if (contactObj == null)
{
return NotFound();
}
await ContactsRepo.Update(item);
return NoContent();
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(string id)
{
await ContactsRepo.Remove(id);
return NoContent();
}
}
I have tried both type of connection string:
a)
"connectionstrings": {
"Default": "Server = .\\SQL2017Express; Database = ContactDB; Integrated Security = True;"
},
b)
"connectionstrings": {
"Default": "Server = .\\SQL2017Express; Database = ContactDB; User Id=sa; Password=xxxxx ; Integrated Security = True;"
},
Looks to me like you have an issue connecting to the SQL instance. Unless you have more than one instance of SQL server running you would best un-install the software and install it as the default instance. This just makes configurations so much easier.
If you have SSMS (Sql Server Management Studio) installed on your webserver then open and type select ##servername this gives the name of your actual instance.
You can also see the name in the service, I have a default instance, hence you see no name
Please make sure you know the performance and size limitations of SQL server express before you deploy this too production.
Have you had a look at SQL Server configuration manager (you may need to install it)
You need to make sure that if you're not using TCP but DMA or Pipes to use the proper connection string; have you considered:
\\<computer_name>\pipe\MSSQL$<instance_name>
Have a look at this URL.

Web Api Cross Domain Basic Authentication

I have setup a web api to allow cross domain access with Basic Authentication. When I make a cross domain GET request to the API, it works fine and I am getting token in "Authorization" header in my custom message handler. But when initiating a cross domain POST request, I am not getting the "Authorization" header that's why unable to validate the request.
Any help would be highly appreciated.
Following is the code for my custom message handler for cross domain access.
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace MyWebApi.Handlers
{
public class XHttpMethodOverrideDelegatingHandler : DelegatingHandler
{
static readonly string[] HttpOverrideMethods = { "PUT", "DELETE" };
static readonly string[] AccessControlAllowMethods = { "POST", "PUT", "DELETE" };
private const string HttpMethodOverrideHeader = "X-HTTP-Method-Override";
private const string OriginHeader = "ORIGIN";
private const string AccessControlAllowOriginHeader = "Access-Control-Allow-Origin";
private const string AccessControlAllowMethodsHeader = "Access-Control-Allow-Methods";
private const string AccessControlAllowHeadersHeader = "Access-Control-Allow-Headers";
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var authHeader = request.Headers.Authorization;
if (authHeader == null || authHeader.Scheme != "Basic" || string.IsNullOrWhiteSpace(authHeader.Parameter))
{
return CreateUnauthorizedResponse();
}
if (request.Method == HttpMethod.Post && request.Headers.Contains(HttpMethodOverrideHeader))
{
var httpMethod = request.Headers.GetValues(HttpMethodOverrideHeader).FirstOrDefault();
if (HttpOverrideMethods.Contains(httpMethod, StringComparer.InvariantCultureIgnoreCase))
request.Method = new HttpMethod(httpMethod);
}
var httpResponseMessage = base.SendAsync(request, cancellationToken);
if (request.Method == HttpMethod.Options && request.Headers.Contains(OriginHeader))
{
httpResponseMessage.Result.Headers.Add(AccessControlAllowOriginHeader, request.Headers.GetValues(OriginHeader).FirstOrDefault());
httpResponseMessage.Result.Headers.Add(AccessControlAllowMethodsHeader, String.Join(", ", AccessControlAllowMethods));
httpResponseMessage.Result.Headers.Add(AccessControlAllowHeadersHeader, HttpMethodOverrideHeader);
httpResponseMessage.Result.StatusCode = HttpStatusCode.OK;
}
//No mater what the HttpMethod (POST, PUT, DELETE), if a Origin Header exists, we need to take care of it
else if (request.Headers.Contains(OriginHeader))
{
httpResponseMessage.Result.Headers.Add(AccessControlAllowOriginHeader, request.Headers.GetValues(OriginHeader).FirstOrDefault());
}
return httpResponseMessage;
}
private Task<HttpResponseMessage> CreateUnauthorizedResponse()
{
var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
response.Headers.Add("WWW-Authenticate", "Basic");
var taskCompletionSource = new TaskCompletionSource<HttpResponseMessage>();
taskCompletionSource.SetResult(response);
return taskCompletionSource.Task;
}
}
}
And i have registered the above handler in Application_Start as follows:
namespace MyWebApi
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional});
GlobalConfiguration.Configuration.MessageHandlers.Add(new XHttpMethodOverrideDelegatingHandler());
GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonpMediaTypeFormatter());
}
}
}
At client side on a different domain project, I am trying to add a new record using following code.
AddUser {
var jsonData = {
"FirstName":"My First Name",
"LastName": "My Last Name",
"Email": "my.name#mydomain.com",
"Password": "MyPa$$word"
};
$.ajax({
type: "POST",
dataType: 'json',
url: "http://localhost:4655/api/user/signup",
beforeSend: function (xhr) { xhr.setRequestHeader("Authorization", "Basic xxxxxxxxxxxxxx"); },
accept: "application/json",
data: JSON.stringify(jsonData),
success: function (data) {
alert("success");
},
failure: function (errorMsg) {
alert(errorMsg);
},
error: function (onErrorMsg) {
alert(onErrorMsg.statusText);
},
statusCode: function (test) {
alert("status");
}
});
});
And following is the code for my user controller.
namespace MyWebApi.Controllers
{
public class UserController : ApiController
{
[HttpPost]
[ActionName("Adduser")]
public int Post(UserModel source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
Db.Users.Add(source);
Db.SaveChanges();
return source.UserId;
}
}
}
Thanks in advance!
I've discovered that if I include basic auth credentials in my cross-domain (POST) XHR request, the browser (IE, Chrome, Firefox) rejects the request before it ever gets to my server - and this is true even if I specify withCredentials:true in my initial $.ajax() request. I'm guessing that there's probably something in the CORS spec which requires this. But I think the short answer is that you can't specify basic auth in CORS requests.
Of course, you can get around this in other ways, by passing userids and passwords as part of the URL proper, so I'm not entirely clear what they think they're gaining by restricting it, but presumably they have some reason.
You need to decorate your controller with [HttpOptions]as well as [HttpPost]. Otherwise when it makes a request using the OPTIONS verb, it will throw a 404. So your controller would be
[HttpPost]
[HttpOptions]
[ActionName("Adduser")]
public int Post(UserModel source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
Db.Users.Add(source);
Db.SaveChanges();
return source.UserId;
}

Resources