NetSuite SuiteTalk Joined Search on Custom Objects - c#

I have two custom objects. The parent object is an Order, and it has a list of type Part associated with it. I'm trying to get the list of Parts with a value "Inventory Item" in one field, but only for a particular Order.
I have the name and numeric internalId of the Order available,but the problem is those aren't (as far as I can tell) searchable "fields", there is no "internalId" to provide to tell it to search on one of those.
In the code below, if I don't include the attempt to search on the Order it works fine. I can get all the Parts with a type of "Inventory Item".
I think I need to use a joined search? My attempt at it is below. This produces a SOAP error at runtime:
Additional information: org.xml.sax.SAXException: 'customizationRef' on
{urn:common_2015_1.platform.webservices.netsuite.com}CustomSearchJoin is
required
Code
private static SearchResult NetSuiteSearchOrderItems(NetSuiteService netSuiteService, string order_name, string order_internalId)
{
CustomRecordSearch searchParts = new CustomRecordSearch();
CustomRecordSearchBasic basicRecordSearchParts = new CustomRecordSearchBasic();
basicRecordSearchParts.recType = new RecordRef { internalId = "64" }; //"Order Item" NetSuite Type ID
//adding a search filter on a String Custom Field
SearchStringCustomField partTypeFieldSearch = new SearchStringCustomField();
partTypeFieldSearch.#operator = SearchStringFieldOperator.#is;
partTypeFieldSearch.internalId = "896"; //ID of the Part object's "Type" field
partTypeFieldSearch.operatorSpecified = true;
partTypeFieldSearch.searchValue = "Inventory Item"; //only want inventory item parts
//Show me only parts on a certain Order
CustomSearchJoin orderSearchJoin = new CustomSearchJoin();
CustomRecordSearchBasic orderBasicSearch = new CustomRecordSearchBasic();
orderBasicSearch.recType = new RecordRef { internalId = "56" }; //"Order" NetSuite Type ID
//-- WHAT GOES HERE INSTEAD OF SearchStringCustomField?
//adding a search filter on a String Custom Field
SearchStringCustomField fsoItemTypeSearch = new SearchStringCustomField();
fsoItemTypeSearch.#operator = SearchStringFieldOperator.#is;
fsoItemTypeSearch.internalId = "name"; //I have the "name" and numeric "internal id" of the order available.
fsoItemTypeSearch.operatorSpecified = true;
fsoItemTypeSearch.searchValue = order_name;//search filter value
orderSearchJoin.searchRecordBasic = orderBasicSearch;
//add in the things we want to search on
basicRecordSearchParts.customFieldList = new SearchCustomField[] { partTypeFieldSearch };
//add the basic search and join search and then perform the search
searchParts.basic = basicRecordSearchParts;
searchParts.customSearchJoin = new CustomSearchJoin[] { orderSearchJoin };
SearchResult response = netSuiteService.search(searchParts);
return response;
}

Related

What is the process for adding Groups to an existing type in Kontent Management API v2 with C#?

If I simply attempt to add a Group using the Kontent Management API (v2) to an existing Kontent type I get the following error (see code below which generates this error):
Validation errors: 511 Every element should have a correct content group reference when using groups
What is the process for adding a group via the Management API in this case using C#? I would assume that I need to add a group Reference to all the existing elements first, but how do I do that when I cannon add the group through the API? Can I create a valid Reference simply using a new ContentGroupModel() object before adding it to the actual type in Kontent?
Here is my existing code, which throws the above error:
var updatedType = await service.ModifyContentTypeAsync(
Reference.ById(existingContentType.Id),
new ContentTypeAddIntoPatchModel
{
Path = "/content_groups",
Value = new ContentGroupModel
{
Name = "New Group",
CodeName = "new_group"
}
}
);
using Kentico.Kontent.Management;
var client = new ManagementClient(new ManagementOptions
{
ApiKey = "<YOUR_API_KEY>",
ProjectId = "<YOUR_PROJECT_ID>"
});
var identifier = Reference.ById(Guid.Parse("0be13600-e57c-577d-8108-c8d860330985"));
// var identifier = Reference.ByCodename("my_article");
// var identifier = Reference.ByExternalId("my-article-id");
var response = await client.ModifyContentTypeAsync(identifier, new ContentTypeOperationBaseModel[]
{
new ContentTypeReplacePatchModel
{
Path = "/name",
Value = "A new type name"
},
new ContentTypeReplacePatchModel
{
Path = "/elements/codename:my_text_element/guidelines",
Value = "Here you can tell users how to fill in the element."
},
new ContentTypeAddIntoPatchModel
{
Path = "/elements",
Value = new TextElementMetadataModel
{
Name = "My title",
Guidelines = "Title of the article in plain text.",
ExternalId = "my-title-id",
},
},
new ContentTypeRemovePatchModel
{
Path = "/elements/id:0b2015d0-16ae-414a-85f9-7e1a4b3a3eae"
}
});
Refer - https://docs.kontent.ai/reference/management-api-v2#operation/modify-a-content-type
I was able to work it out. It turns out you can use the Codename of the new group, even before adding it in Kontent, as a reference. Then give that reference to the content_group property/path for existing elements in the model.
Here is the code I have now that adds the new Group and adds it to all existing elements dynamically:
using Kentico.Kontent.Management.Models.Shared;
using Kentico.Kontent.Management.Models.Types;
using Kentico.Kontent.Management.Models.Types.Patch;
//
// ...
//
// First, Get type from the client. Then ...
var elementCodenames = myType.Elements.Select(el => el.Codename);
// Create reference to the codename of the group to be created
var groupRef = Reference.ByCodename("new_group");
// Create the modify models
var modifyModels = new ContentTypeOperationBaseModel[] {
new ContentTypeAddIntoPatchModel
{
Path = "/content_groups", // Add to content_groups path
Value = new ContentGroupModel
{
Name = "New Group",
CodeName = "new_group" // Same codename as above
}
}
}
.Concat(
// Dynamically add new group, by ref, to existing elements
elementCodenames.Select(codename => new ContentTypeReplacePatchModel
{
Path = $"/elements/codename:{codename}/content_group",
Value = groupRef // Group reference created above from the codename
})
);
// Make call to add new group AND link group to elements in the one call
var response = await client.ModifyContentTypeAsync(myTypeIdentifier, modifyModels.ToArray());
Leaving out other details, like retrieving the existing type, but for reference here are the API Docs: https://docs.kontent.ai/reference/management-api-v2

How to send multi-select attribute values to Acumatica

I can't find an example of this here or in the Acumatica sample code. Sending single attribute values works fine, but I can't find a way to send multi-select ones. They are returned as a comma-separated list in a string value, but sending them that way doesn't work. Also, sending them as multiple instances of single values doesn't work.
Here's what I've tried. (In the actual code I'm sending some other single attributes in the list, as well, but those work fine.)
// this results in nothing being set for the attribute
string interestedIn = "Brochure, Contact, Collecting small stones";
List<Acumatica.AttributeDetail> attributes = new List<Acumatica.AttributeDetail>();
attributes.Add(
new Acumatica.AttributeDetail {
Attribute = new Acumatica.StringValue { Value = "Interested in" },
Value = new Acumatica.StringValue { Value = interestIn }
}
);
custAdd.Attributes = attributes.ToArray();
// this results in the last item in the list being set for the attribute
string interestedIn = "Brochure, Contact, Collecting small stones";
List<Acumatica.AttributeDetail> attributes = new List<Acumatica.AttributeDetail>();
string[] interests = Convert.ToString(interestedIn).Split(',');
foreach (string interest in interests) {
attributes.Add(
new Acumatica.AttributeDetail {
Attribute = new Acumatica.StringValue { Value = "Interested in" },
Value = new Acumatica.StringValue { Value = interest.Trim() }
}
);
};
custAdd.Attributes = attributes.ToArray();
From the source
MappedCustomer obj = bucket.Customer;
Core.API.Customer impl = obj.Local;
impl.Attributes = impl.Attributes ?? new List<AttributeValue>();
AttributeValue attribute = new AttributeValue();
attribute.AttributeID = new StringValue() { Value = attributeID };
attribute.ValueDescription = new StringValue() { Value = attributeValue?.ToString() };
impl.Attributes.Add(attribute);
Some subtle differences here. Also, I wonder if the .ToArray() call is necessary.

How to search for "Lot Numbered Assembly/Bill of Materials" items by external ID using SuiteTalk

WSDL 2014.2. C# in Visual Studio 2013.
I can get "Inventory Item" items using this code:
var recordRefs = new List<RecordRef>();
foreach (string externalId in ExternalIds)
{
recordRefs.Add(new RecordRef
{
externalId = externalId,
type = RecordType.inventoryItem,
typeSpecified = true
});
}
var request = new ItemSearchBasic
{
externalId = new SearchMultiSelectField
{
#operator = SearchMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = recordRefs.ToArray()
}
};
SearchResult response = SuiteTalkConnection.Service.search(request);
However, if I change type = RecordType.inventoryItem to type = RecordType.lotNumberedAssemblyItem then I don't get results. I am specifying correct values as external IDs.
I have tried all sorts of variations on the above, including ItemSearch instead of ItemSearchBasic but I never get results.
It doesn't help that I can find zero NetSuite documentation on searching for specific item types.
I think answer to this question would also work in this case. Try using type as "assemblyItem" only and use an additional searchFilter "isLotItem" and set it to true.

Dynamic Search using Suitetalk

I am trying to create a C# application (Using suitetalk) that would allow us to search through Netsuite records. The record type will be specified dynamically. Please can you help?
I have checked the webservices and identified that SearchRecord class has many sub classes of type AccountSearch, ItemSearch, etc.
However, I wanted to do these searches dynamically.
AccountSearch acc = new AccountSearch();
SearchResult searchresult = new SearchResult();
searchresult = _service.search(acc);
The above code gives me the list of accounts. But, the AccountSearch is hardcoded here.
The piece of code below works.
SearchRecord search;
SearchRecord searchCriteria;
SearchRecordBasic searchBasicCriteria;
if(recordType.equals(RecordType.account)){
search = new AccountSearchAdvanced();
searchCriteria = new AccountSearch();
searchBasicCriteria = new AccountSearchBasic();
//set criteria on searchBasicCriteria
((AccountSearch) searchCriteria).setBasic((AccountSearchBasic) searchBasicCriteria);
((AccountSearchAdvanced) search).setCriteria((AccountSearch) searchCriteria);
}else if(recordType.equals(RecordType.customer)){
search = new CustomerSearchAdvanced();
searchCriteria = new CustomerSearch();
searchBasicCriteria = new CustomerSearchBasic();
//set criteria on searchBasicCriteria
((CustomerSearch) searchCriteria).setBasic((CustomerSearchBasic) searchBasicCriteria);
((CustomerSearchAdvanced) search).setCriteria((CustomerSearch) searchCriteria);
}else{
search = null;
}
if(search != null) _service.search(search);
But I think a better solution would be to create specific methods for each type of search. That way the code is more readable, plus you avoid all that casting. Then you would have to handle the returned RecordList for each specific record type.

How to Add Custom Fields on JoeBlogs Wordpress Wrapper

I'm trying to complete fields with JoeBlogs WordPress Wrapper.
My code is:
private void postToWordpress(string title, string postContent,string tags, string aioTitle)
{
string link = this.maskedTextBox1.Text;
string username = this.maskedTextBox2.Text;
string password = this.maskedTextBox3.Text;
var wp = new WordPressWrapper(link + "/xmlrpc.php", username, password);
var post = new Post();
post.Title = title;
post.Body = postContent;
post.Tags = tags.Split(',');
string[] cf = new CustomField(); //{ ID = "name", Key = "aiosp_title", Value = "All in One SEO Title" };
cf.ID = "name";
cf.Key = "aiosp_title";
cf.Value = "All in One SEO Title";
post.CustomFields[0] = cf;
wp.NewPost(post, false);
}
The error is at this line:
post.CustomFields[0] = cf;
And it is:
An unhandled exception of type 'System.NullReferenceException'
occurred in JoeBlogsWordpressWrapperTests.exe
Additional information: Object reference not set to an instance of an
object.
So, how to use/add correctly custom fields on WordPress from C# Application using JoeBlogs WordPress Wrapper?
The following code fixes your NullReferenceException and also successfully saves the custom fields into the Post in Wordpress.
private void postToWordpress(string title, string postContent,string tags, string aioTitle)
{
string link = this.maskedTextBox1.Text;
string username = this.maskedTextBox2.Text;
string password = this.maskedTextBox3.Text;
var wp = new WordPressWrapper(link + "/xmlrpc.php", username, password);
var post = new Post();
post.Title = title;
post.Body = postContent;
post.Tags = tags.Split(',');
var cfs = new CustomField[]
{
new CustomField()
{
// Don't pass in ID. It's auto assigned for new custom fields.
// ID = "name",
Key = "aiosp_title",
Value = "All in One SEO Title"
}
};
post.CustomFields = cfs;
wp.NewPost(post, false);
}
You were getting the NullReferenceException error because you were creating a string array and trying to assign it the CustomFields property of the Post object, which is an array of CustomField i.e. CustomField[].
Also, in order to save the CustomFields to the Post in the database, you should pass in only the Key and Value fields of the CustomField struct and skip the ID field all together. Reason being Wordpress auto-generates the ID fields (also it's an integer / numeric field in the database). I think that was what was causing the XmlRpc call to fail, but we did not get any errors as to why.
Try the above code and it should work (I have it working on my localhost WAMP Wordpress installation).
One final note. Although the CustomField's name property is called Key, it doesn't have to be unique, and uniqueness is not enforced. So for instance, if you are populating a custom dropdown box with a list of cities for a Post, you could have the list of cities as a set of custom fields as follows.
var cfs = new CustomField[]
{
new CustomField()
{
Key = "aiosp_title",
Value = "All in One SEO Title"
} ,
new CustomField()
{
Key = "this is another custom field with HTML",
Value = "All in One SEO Title <br/> Keyword 1 <br/><p>This is some more text and html</p>"
} ,
new CustomField()
{
Key = "list_of_cities",
Value = "San Francisco"
} ,
new CustomField()
{
Key = "list_of_cities",
Value = "New York"
}
};
This will also get saved to the post, with 2 custom fields with the same Key value and different text in the Value field's value.
And last but not least, you can store HTML also in the custom fields (as shown above).

Categories