Acumatica - Add Required Attribute to new Case through Rest API - json

I am trying to create new Acumatica cases through the REST API. There is an attribute on the cases that I am trying to populate. I created a Detail object for the case attributes like this.
Case Attributes
I then tried to populate a test case using the below code.
public async Task CreateTestCase()
{
Case newCase = new Case
{
ClassID = new JsonObject<string> { value = this.DepartmentToClassID["1"] },
DateReported = new JsonObject<DateTime> { value = DateTime.Now },
BusinessAccount = new JsonObject<string> { value = this.UserOrganizationToBusinessAccount["67"] },
LastActivityDate = new JsonObject<DateTime> { value = DateTime.Now },
Owner = new JsonObject<string> { value = this.OwnerToEmployee["43"] },
ClosingDate = new JsonObject<DateTime?> { value = null },
Severity = new JsonObject<string> { value = this.PriorityToSeverity["1"] },
Status = new JsonObject<string> { value = this.Status["1"] },
Subject = new JsonObject<string> { value = "Test Case" },
Attributes = new List<CaseAttribute>
{
new CaseAttribute
{
AttributeID = new JsonObject<string> { value = "Kayako Ticket Number" },
Value = new JsonObject<string> { value = "12345" }
}
}
};
var json = JsonConvert.SerializeObject(newCase);
try
{
var response = await _httpClient.PutAsync("Custom/1.0/Case", new StringContent(json, Encoding.UTF8, "application/json"));
string res = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return;
}
}
JsonObject just adds the { "value": } object that Acumatica requires. Here is the JSON string that results from the above code.
{
"ClassID": {
"value": "SUPPORT"
},
"Contact": null,
"DateReported": {
"value": "2017-07-10T00:41:45.045008-07:00"
},
"BusinessAccount": {
"value": "24SEVEN"
},
"LastActivityDate": {
"value": "2017-07-10T00:41:45.045008-07:00"
},
"Owner": {
"value": "JSS"
},
"ClosingDate": {
"value": null
},
"Severity": {
"value": "Medium"
},
"Status": {
"value": "Open"
},
"Subject": {
"value": "Test Case"
},
"Attributes": [{
"AttributeID": {
"value": "Kayako Ticket Number"
},
"Value": {
"value": "12345"
}
}]
}
This matches the case object when I run a GET. The response that comes back is "500 Internal Server Error" and there are two errors:
CR Error: There are empty required attributes: 'Kayako Ticket Number'
Case.Attributes[1].Value: 'Kayako Ticket Number' cannot be empty.
When I create the attribute, it is created at index 0. The error indicates it is looking at index 1. Is there a way for me to load the attribute based on the attribute string and not the index number? Is it possible the attribute is not even being loaded at all?
I also tried using the KAYAKONUMB_Attributes dynamic field to load the entry. The GET successfully returned the following:
"KayakoTicketNumber": {
"value": "12002"
},
The PUT, however, returned the first error above, but not the second one. My JSON string matched the GET response.
Update
Following Serg's advice, I extended the default endpoint, and now I am able to load two of the three attributes I have. The last attribute I am trying to load is a combo box. It seems like there needs to be a different way to load that particular attribute.
Here is my current endpoint setup:
And here is my new object initializer:
Case newCase = new Case
{
ClassID = new JsonObject<string> { value = this.DepartmentToClassID["3"] },
Contact = new JsonObject<int> { value = 22322 },
DateReported = new JsonObject<DateTime> { value = DateTime.Now },
BusinessAccount = new JsonObject<string> { value = this.UserOrganizationToBusinessAccount["218"] },
LastActivityDate = new JsonObject<DateTime> { value = DateTime.Now },
Owner = new JsonObject<string> { value = this.OwnerToEmployee["43"] },
ClosingDate = new JsonObject<DateTime?> { value = null },
Severity = new JsonObject<string> { value = this.PriorityToSeverity["1"] },
Status = new JsonObject<string> { value = this.Status["1"] },
Subject = new JsonObject<string> { value = "Test Case" },
Attributes = new List<CaseAttribute<string>>
{
new CaseAttribute<string>
{
AttributeID = new JsonObject<string> { value = "Kayako Ticket Number" },
Value = new JsonObject<string> { value = "12345" }
},
new CaseAttribute<string>
{
AttributeID = new JsonObject<string> { value = "Case Reply Due Date" },
Value = new JsonObject<string> { value = "2010-10-30 00:00:00.000" }
},
new CaseAttribute<string>
{
AttributeID = new JsonObject<string> { value = "Upgrade Stage" },
Value = new JsonObject<string> { value = "7. Test In Progress"}
}
}
};
The error message being returned is:
PX.Data.PXException: CR Error: There are empty required attributes: 'Upgrade Stage'
Any help would be greatly appreciated, thank you.

I was able to solve the issue by creating a linked object and setting the attribute that way. Here is the endpoint setup (sorry for the linked images, my rep is still too low):
Linked Entity
And here is the code for creating the case object. I may need to tweak the endpoint and the objects as currently it is linking to an array of attributes but returning only the first one. I am hoping there is a way to return individual attributes.
Case newCase = new Case
{
ClassID = new JsonObject<string> { value = this.DepartmentToClassID["1"] },
Contact = new JsonObject<int> { value = 22322 },
DateReported = new JsonObject<DateTime> { value = DateTime.Now },
BusinessAccount = new JsonObject<string> { value = this.UserOrganizationToBusinessAccount["218"] },
LastActivityDate = new JsonObject<DateTime> { value = DateTime.Now },
Owner = new JsonObject<string> { value = this.OwnerToEmployee["43"] },
ClosingDate = new JsonObject<DateTime?> { value = null },
Severity = new JsonObject<string> { value = this.PriorityToSeverity["1"] },
Status = new JsonObject<string> { value = this.Status["1"] },
Subject = new JsonObject<string> { value = "Test Case" },
KayakoTicket = new CaseAttribute
{
AttributeID = new JsonObject<string> { value = "Kayako Ticket Number" },
Value = new JsonObject<int> { value = 12345 }
}
};
Finally, here is a screenshot of the case in Acumatica.
Created Case

Unfortunately, attributes are a special case (always). From what I see, you've mapped your own CaseAttribute object for this - and, unfortunately, this won't work without special actions. Try using AttributeValue entity from Acumatica's Default endpoint (and, of course, make sure that your endpoint extends Default), that might help you.

Related

Groovy Modifying Json

I'm trying to modify a json which I'm reading from a file:
String response = new File("response.json").text
here is what response looks like:
{
"TerminatingInstances": [
{
"InstanceId": "id",
"CurrentState": {
"Code": 32,
"Name": "shutting-down"
},
"PreviousState": {
"Code": 16,
"Name": "running"
}
}
]
}
To modify it I did as follows:
def slurped = new JsonSlurper().parseText(response)
def builder = new JsonBuilder(slurped)
builder.content.TerminatingInstances.InstanceId = "id-updated"
but I get argument type mismatch error, not sure why.
That's because it's an array of instances inside TerminatingInstances
You can either set all of them to the new id with *.:
builder.content.TerminatingInstances*.InstanceId = "id-updated"
Or set just the first one with
builder.content.TerminatingInstances[0].InstanceId = "id-updated"

List as Json encoding returns Last index value

here i have added the value to the contactList.listChild but the the string list doesn't return all the value to the parent list it only returns the last added value. i know its a simple mistake though i couldn't figure out where am making mistake.
any suggestion would be helpful.
ContactName contactList = new ContactName();
createJson(){
for(int index = 0; index < _contacts.length; index++){
Contact contact = _contacts?.elementAt(index);
List<Item> numbersList = contact.phones.toList();
for(int i = 0; i < numbersList.length; i++ ){
contactList.listChild = [numbersList[i].value];
}
contactList.name = contact.displayName;
lisJson.add(contactList.toJson());
}
var data = {
"data" : lisJson
};
var body = json.encode(data);
print("$body");
}
// here is my model class
class ContactName extends Object {
String name;
List<String> listChild = new List<String>();
Map toJson() => {"name":name, "phone":listChild};
}
this is what am getting from this
{
"data": [
{
"name": "user1",
"phone": [
"8221551458"
]
},
{
"name": "user2",
"phone": [
"1234567890"
]
}
]
}
// and the output should be something like this
{
"data": [
{
"name": "user1",
"phone": [
"8221551458"
]
},
{
"name": "user2",
"phone": [
"8220780548"
"1234567890"
]
}
]
}
You need to replace
contactList.listChild = [numbersList[i].value]
by
contactList.listChild.add(numbersList[i].value)
Your code is creating a new list on every iteration of the for statement, that's why the last element is the only one you see.
Be sure to initialize contactList.listChild before using it or will throw an exception when trying to add a new element.

Not reading the second data from json

I am trying to read data from json (2 cycles) and add an entry. the first time, the data added successfully but during the second cycle, it failed. below are the details. Also working when run with page object and without reading from json;
The "test_pom_fromjson" added successfully but when running for the test_pom_fromjson_first it failed with error saying no element found using element(by.id('s2id_autogen10')).click(); which is the locator for location column.
Refer the screenshot and html code for the page - https://pastebin.com/ZXyRx1tv
mo.ts: pageobject for the below spec file
var mo = function () {
this.createbutton = function () {
var createb = element(by.css('button[title="Add Master Obligation"]'))
//var createbutton = element(by.buttonText('↵ ↵ Add Master Obligatio...↵ '))
browser.executeScript("arguments[0].scrollIntoView();arguments[1].click();", createb, createb);
}
this.MasterObligationName = function (value) {
element(by.model('obligation.ObligationName')).sendKeys(value);
}
this.Module = function (value) {
element(by.id('s2id_TaxProcess')).click();
element.all(by.repeater('item in obligation.TaxProcess.list')).get(value).click();
}
this.Location = function (value) {
element(by.id('s2id_autogen10')).click();
element.all(by.repeater('item in obligation.Jurisdiction.list')).get(value).click();
}
this.CentralObligation = function (value) {
element.all(by.model('obligation.CentralObligation')).get(value).click();
}
this.Type = function (value) {
element(by.id('s2id_txtReturnType')).click();
element.all(by.repeater('item in obligation.ReturnType.list')).get(value).click();
}
this.Years = function (value) {
element(by.id('s2id_txtTaxYear')).click();
element.all(by.repeater('item in obligation.TaxYears.list')).get(value).click();
}
this.Periods = function (value) {
element(by.id('s2id_txtPeriod')).click();
element.all(by.repeater('tem in obligation.Periods.list')).get(value).click();
}
this.Forms = function (value) {
element.all(by.id('s2id_txtForms')).get(0).click();
element.all(by.repeater('item in obligation.Forms.list')).get(value).click();
}
this.Reports = function (value) {
element.all(by.id('s2id_txtForms')).get(1).click();
element.all(by.repeater('item in obligation.Reports.list')).get(value).click();
}
this.savebutton = function () {
var saveb = element(by.css('button[title="Save"]'))
browser.executeScript("arguments[0].scrollIntoView();arguments[1].click();", saveb, saveb);
}
module.exports = new mo();
spec.ts: var mo = require("../page/mo.ts")
var testData = require('../testdata/testdata.json');
testData.forEach(function (data) {
it('create and save master obligation', function () {
browser.sleep(10000);
mo.createbutton();
browser.sleep(10000);
mo.MasterObligationName(data.Master_Obligation_Name)
mo.Module(data.Module);
mo.Location(data.Location);
mo.CentralObligation(data.Central_Obligation);
mo.Type(data.Type);
mo.Years(data.Years);
mo.Periods(data.Periods);
mo.Forms(data.Forms);
mo.Reports(data.Reports);
mo.savebutton();
})
})
testdata.json:
[{
"Master_Obligation_Name": "test_pom_fromjson",
"Module": "2",
"Location": "1",
"Central_Obligation": "0",
"Type": "2",
"Years": "1",
"Periods": "1",
"Forms": "1",
"Reports": "0"
},
{
"Master_Obligation_Name": "test_pom_fromjson_first",
"Module": "2",
"Location": "1",
"Central_Obligation": "0",
"Type": "2",
"Years": "1",
"Periods": "1",
"Forms": "1",
"Reports": "0"
}]
Two options you can try:
1.Wait for some time before that click
2.Either id number is auto generated so it might be changed everytime, so use some other locator.
If the id is auto-generated then look for some other locator then:
element.all(by.id('s2id_Jurisdiction')).get(1).click();
element.all(by.repeater('item in obligation.Jurisdiction.list')).get(value).click();
OR you can also try the following way:
var module = element(by.xpath("//input[starts-with(#id, 's2id_autogen')]")).get(1).click();
var location= element(by.xpath("//input[starts-with(#id, 's2id_autogen')]")).get(2).click();
var type = element(by.xpath("//input[starts-with(#id, 's2id_autogen')]")).get(3).click();

How to access nested objects in json by angularjs $resource

I have .NET WCF service providing REST service. Everything works for me, until I am trying to send object with nested objects. Then I am getting nothing in angularjs. How can I use/access nested object for data exchange?
.NET service part:
[OperationContract] // route prefix 'api'
[WebGet(UriTemplate = "users/{id}/privileges", ResponseFormat = WebMessageFormat.Json)]
public PrivilegeSet GetPrivileges(string id)
{
var response = new PrivilegeSet();
List<Role> roles = new List<Role>();
roles.Add(new Role() { RoleId = 1, Name = "Role 1", Active = true });
roles.Add(new Role() { RoleId = 2, Name = "Role 2", Active = true });
roles.Add(new Role() { RoleId = 3, Name = "Role 3", Active = false });
response.Roles = roles;
List<SubRole> subRoles = new List<SubRole>();
subRoles.Add(new SubRole() { SubRoleId = 1, Name = "SubRole 1", RoleId = 1, Active = true });
subRoles.Add(new SubRole() { SubRoleId = 2, Name = "SubRole 2", RoleId = 1, Active = true });
subRoles.Add(new SubRole() { SubRoleId = 3, Name = "SubRole 3", RoleId = 1, Active = false });
response.SubRoles = subRoles;
return response;
}
JSON structure:
{
"Roles": [
{
"Active": true,
"Name": "Role 1",
"RoleId": 1
},
{
"Active": true,
"Name": "Role 2",
"RoleId": 2
},
{
"Active": false,
"Name": "Role 3",
"RoleId": 3
}
],
"SubRoles": [
{
"Active": true,
"Name": "SubRole 1",
"RoleId": 1,
"SubRoleId": 1
},
{
"Active": true,
"Name": "SubRole 2",
"RoleId": 1,
"SubRoleId": 2
},
{
"Active": false,
"Name": "SubRole 3",
"RoleId": 1,
"SubRoleId": 3
}
]
}
Angularjs service:
angular.module('privilegeService', ['ngResource']).
factory('Privilege', function ($resource) {
return $resource('api/users/:userId/privileges', {userId: '#id'});
});
Angularjs fetching part:
function PrivilegesCtrl($scope, Privilege) {
$scope.privileges = Privilege.query({userId:2}); // privileges remains empty using nested objects, with one level object works fine
...
Why privileges remains empty when JSON has nested objects? And how to access nested objects in the view?
When you use the $resource service the .query action assumes your response is an array. You can specify that the response is not an array when using .query by specifying it when creating the resource with the third parameter below:
angular.module('privilegeService', ['ngResource']).
factory('Privilege', function ($resource) {
return $resource('api/users/:userId/privileges',
{userId: '#id'},
{'query': {method:'GET', isArray:false}});
});
Check out this plnkr for an example. If you take out the {'query': {method:'GET', isArray:false}} your response will be an empty array.
Note 1: your console is likely showing an error TypeError: Object #<Resource> has no method 'push' which, when working with .query, usually means an array is expected from your REST call.
Note 2: the resource action defaults are described in the $resource documentation as follows:
{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'} };

Passing Json from action to view by ViewBag

I'm trying to get the result below using JsonResult, but I can't
var localJSON = [
{ "id": "1", "label": "tagName1", "value": "tagValue1" },
{ "id": "2", "label": "tagName2", "value": "tagValue2" },
{ "id": "3", "label": "tagName3", "value": "tagValue3" },
{ "id": "1553", "label": "tagName1553", "value": "tagValue1553" }
];
Here is the way I use:
controller
private JsonResult GetAvailableTags()
{
var tagsList = Facade.Tags.Get(CurrentLocale.ID);
var retValue = new
{
id = tagsList.Select(x => x.ID).ToArray(),
label = tagsList.Select(x => x.Name).ToArray(),
value = tagsList.Select(x => x.Name).ToArray()
};
return Json(retValue);
}
public ActionResult AddPhoto()
{
var availblableTags = GetAvailableTags();
JavaScriptSerializer serializer = new JavaScriptSerializer();
ViewBag.AvailableTags = serializer.Serialize(availblableTags.Data);
return View();
}
view
var localJSON = [ #Html.Raw(ViewBag.AvailableTags)];
The result is
var localJSON = [
{"id":[1,2,3,1553],"label":["tagName1","tagName2","tagName3","tagName1553" ],"value":["tagName1","tagName2","tagName3","tagName1553" ]}
];
What should I do to resolve that?
I assume you want to get x.Value for value in JSON? Then change your assignment for retValue to
var retValue = tagsList.Select(
x => new
{
id = x.Id,
label = x.Name,
value = x.Value
}).ToArray();
In your retValue assignment code you were creating a single object of anonymous type with array-typed members id, label and value. For the output you want you need to create an array, each member of which is an object with simple fields id, name and value.