I search hi and lo but could not find any decent reference how to get paid and outstanding amounts for the invoice in NetSuite using SuiteTalk API.
Documentation is non-existent and so are the samples so I'm poking in the dark here.
When invoice is retrieved using get method, I kind of expected these to be specified (fields amountPaid and amountRemaining) but they are not.
The next port of call, unfortunately, is to search for customer payments that have been applied to the target invoice. Search works but, to make the matter worse, each payment is returned without any apply details so extra call is required to get all apply details and figure out the total payment amount applied to the invoice.
Is that REALLY the only way to do it?!
(And I probably would have to consider deposits and deposit applications as they are separate record types. Sigh)
Any help is appreciated
George
UPDATE: Working code sample
long internalInvoiceId = 42;
TransactionSearchAdvanced tsa = new TransactionSearchAdvanced()
{
columns = new TransactionSearchRow()
{
basic = new TransactionSearchRowBasic()
{
total = new SearchColumnDoubleField[] { new SearchColumnDoubleField() },
amount = new SearchColumnDoubleField[] { new SearchColumnDoubleField() },
amountPaid = new SearchColumnDoubleField[] { new SearchColumnDoubleField() },
amountRemaining = new SearchColumnDoubleField[] { new SearchColumnDoubleField() }
}
},
criteria = new TransactionSearch()
{
basic = new TransactionSearchBasic()
{
mainLine = new SearchBooleanField()
{
searchValue = true,
searchValueSpecified = true
},
type = new SearchEnumMultiSelectField()
{
#operator = SearchEnumMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new string[] { "_invoice" }
},
internalIdNumber = new SearchLongField()
{
#operator = SearchLongFieldOperator.equalTo,
operatorSpecified = true,
searchValue = internalInvoiceId,
searchValueSpecified = true
}
}
}
};
SearchResult sr = nss.search(tsa);
Do a transaction search.
Criteria:
Main Line: Yes
Type: Invoice
(Optional) Internal ID: "The internal id of your Invoice"
Result/Columns:
Amount Paid
Amount Remaining
Translate the above to SuiteTalk API (TransactionSearchAdvanced) and you should be able to get what you want.
Here's a function using the NetSuite ruby bindings that retrieves the amount remaining (due) on an invoice:
def amount_due_for_invoice(ns_invoice)
search = NetSuite::Records::Invoice.search(
criteria: {
basic: [
{
field: 'type',
operator: 'anyOf',
value: %w(_invoice)
},
{
field: 'mainLine',
value: true
},
{
field: 'internalIdNumber',
operator: 'equalTo',
value: ns_invoice.internal_id
}
]
},
columns: {
'tranSales:basic' => {
'platformCommon:internalId/' => {},
'platformCommon:amountRemaining' => {}
}
}
)
if search.results.size > 1
fail "invoice search on internalId should never return more than a single result"
end
search.results.first.attributes[:amount_remaining].to_f
end
An alternative solution here is to create a custom transaction body formula field which pulls the value of the invoice's amountremainingtotalbox field.
Related
I am trying to assert and print the response, for that need help.
Below is response body:
{
"createdIncidents":[
{
"incidentRef":"I0000000",
"personName":"API API",
"personType":"Patient"
},
{
"incidentRef":"I0000000",
"personName":"Ballarat HelpDesk",
"personType":"Staff"
},
{
"incidentRef":"I0000000",
"personName":"test api",
"personType":"Visitor"
},
{
"incidentRef":"I0000000",
"personName":null,
"personType":"Hazard"
}
]
}
I am trying to print incidentRef and personType together in a string.
For that, I am using this code:
var data = JSON.parse(responseBody);
data.createdIncidents.forEach(function(incident, personT) {
var personType = "personType" + personT.personType;
var incidents = "incidentRef" + incident.incidentRef;
var pt = tests["incidents created for " + personType ] = 'personType';
var inc = tests["incidents number is " + incidents] = 'incidents';
tests["incidents created for" +inc && + pt ];
});
Here it is not reading the second items inside the function.
In a separate function declaration it works fine.
I want to print it as:
"incidentRef": "I0000000 is created for "personType": "Hazard""
This would log each item from the createdIncidents array to the console - Unsure what you're actually trying to assert against though:
_.each(pm.response.json().createdIncidents, (arrItem) => {
console.log(`Incident Ref: ${arrItem.incidentRef} is created for Person Type: ${arrItem.personType}`)
})
Given your response data, this would be the output:
Incident Ref: I0000000 is created for Person Type: Patient
Incident Ref: I0000000 is created for Person Type: Staff
Incident Ref: I0000000 is created for Person Type: Visitor
Incident Ref: I0000000 is created for Person Type: Hazard
This could be wrapped in a pm.test() and the different items can be asserted against using the pm.expect() syntax.
This is very basic and is very hardcoded but it would check the data in your example:
pm.test('Check the response', () => {
_.each(pm.response.json().createdIncidents, (arrItem) => {
pm.expect(arrItem.incidentRef).to.equal("I0000000")
pm.expect(arrItem.personType).to.be.oneOf(['Patient','Staff','Visitor','Hazard'])
console.log(`Incident Ref: ${arrItem.incidentRef} is created for Person Type: ${arrItem.personType}`)
})
})
I have a rally grid that shows defects. I want do add a column that shows the number of days a defect has been open.
I know can do that by adding a custom renderer in the column configs, but I would also like to sort on this column. Unfortunately, the renderer does not change the sorting of the column.
I think I might be able to use the convert() function on the store instead to create a new virtual column (in this case openAgeDays), but I'm not sure how to do this from the constructor--I presume I make some changes to storeConfig?
Does anyone have an example of how to use a convert function (assuming that this is the right way to do it) to add a new virtual, sortable column to a rally grid?
this.grid = this.add({
xtype: 'rallygrid',
model: model,
disableColumnMenus: false,
storeConfig: [...]
As is the answer in the duplicate, you can add a doSort to the column:
{dataIndex: 'Parent', name: 'Parent',
doSort: function(state) {
var ds = this.up('grid').getStore();
var field = this.getSortParam();
console.log('field',field);
ds.sort({
property: field,
direction: state,
sorterFn: function(v1, v2){
console.log('v1',v1);
console.log('v2',v2);
if (v1.raw.Parent) {
v1 = v1.raw.Parent.Name;
} else {
v1 = v1.data.Name;
}
if (v2.raw.Parent) {
v2 = v2.raw.Parent.Name;
} else {
v2 = v2.data.Name;
}
return v1.localeCompare(v2);
}
});
},
renderer: function(value, meta, record) {
var ret = record.raw.Parent;
if (ret) {
return ret.Name;
} else {
meta.tdCls = 'invisible';
return record.data.Name;
}
}
},
I am pretty new to faceted search, so it is kind of difficult for me to wrap my head around this... but here goes:
Pretend I have Item which contains a collection of SubItems and the SubItem has an enum that indicates a status - I have successfully enabled faceted search on categories on both Item and SubItem using this index definition:
public class FacetIndexItems : AbstractIndexCreationTask<Item>
{
public const string FacetId = "facets/Items";
public const string ItemCategoryFacetName = "Category";
public const string SubItemCategoryFacetName = "SubItems_Category";
public FacetIndexItems()
{
Map = items => from item in items
from subItem in item.SubItems
select new
{
Category = item.Category,
SubItems_Category = subItem.Category
};
}
}
and this FacetSetup:
new FacetSetup
{
Id = FacetIndexItems.FacetId,
Facets =
{
new Facet {Name = FacetIndexItems.ItemCategoryFacetName},
new Facet {Name = FacetIndexItems.SubItemCategoryFacetName}
}
}
So far, so good!
Now, pretend that SubItem has a Status property - is there a way to divide the result of each facet into different statuses?
E.g. so that quering this data:
{
Category: "wut",
SubItems: [
{Category: "bim", Status: "good"},
{Category: "bim", Status: "good"},
{Category: "bim", Status: "bad"}
]
}
by item.Category.In("wut") && item.SubItems.Any(s => s.Category.In("bim")) would yield a result like
{
Category: {
"good": 2
"bad": 1
},
SubItems_Category: {
"good": 2
"bad": 1
}
}
I am unsure whether this is actually possible to do with faceted search, and I am definitely open to alternatives if my approach is wrong.
You would have to generate different values for each option, you can't do a facet on more than one field at a time.
But you can generate fields at indexing time, so that does the same thing.
I am trying to update a nested collection using the Patch API. More specifically, consider the following example - a Posts collection:
{
"Title": "Hello RavenDB",
"Category": "RavenDB",
"Content": "This is a blog about RavenDB",
"Comments": [
{
"Title": "Unrealistic",
"Content": "This example is unrealistic"
},
{
"Title": "Nice",
"Content": "This example is nice"
}
]
}
I used the Patch API and Set-based operation docs at http://ravendb.net/docs/client-api/partial-document-updates and http://ravendb.net/docs/client-api/set-based-operations as well as several stackoverflow questions as resources to do a bulk update using set operations and a static index. A requirement is to update the "Title" of a comment only when the previous value was "Nice" and if so, update it to "Bad".
The static index "NicePosts" is defined as:
Map = posts => from post in posts
where post.Comments.Any(comment => comment.Title == "Nice")
select new {post.Title, post.Category}
The bulk patch update command is:
documentStore.DatabaseCommands.UpdateByIndex("NicePosts",
new IndexQuery(),
new[] { new PatchRequest
{ Type = PatchCommandType.Modify,
Name = "Comments",
PrevVal = RavenJObject.Parse(#"{ ""Title"": ""Nice""}"),
Nested = new[]
{
new PatchRequest {Type = PatchCommandType.Set, Name = "Title", Value = new RavenJValue("Bad") },
} }, allowStale: true);
I have some questions regarding this:
1) Is my structure/syntax for the update command correct?
2) I would like the update to be performed on all the records in the collection. Hence I haven't defined the query filter in the IndexQuery Query because the "NicePosts" index already returns the appropriate set. However running this command doesn't update the collection.
3) If I set "allowStale:false" I get a "stale index" error. Before opening my document store session I instantiate the index class and Execute it to persist it to the ravenDB instance. Any ideas whats going wrong here?
Thanks,
EDIT:
Based on ayende's recommendation changed Patch command to:
documentStore.DatabaseCommands.UpdateByIndex("NicePosts",
new IndexQuery(),
new[] {
new PatchRequest {
Type = PatchCommandType.Modify,
Name = "Comments",
Position = 0,
Nested = new[] {
new PatchRequest {Type = PatchCommandType.Set, Name = "Title", Value = new RavenJValue("Bad")},
}
}
}, allowStale: false);
This can now be done using the scripted patch request:
string oldTitle = "Nice";
string newTitle = "Bad";
documentStore.DatabaseCommands.UpdateByIndex("NicePosts",
new IndexQuery(),
new ScriptedPatchRequest
{
Script = #"for (var i = 0; i < this.Comments.length; i++)
if (this.Comments[i].Title == oldTitle)
this.Comments[i].Title = newTitle;",
Values =
{
{ "oldTitle", oldTitle },
{ "newTitle", newTitle },
},
}
);
You can't use the patch command to update values based on the existing value in the array.
You need to specify the actual position.
I would like to start by saying I have read Rally Kanban - hiding Epic Stories but I'm still having trouble on implementing my filter based on the filter process from the Estimation Board app. Currently I'm trying to add an items filter to my query object for my cardboard. The query object calls this._getItems to return an array of items to filter from. As far as I can tell the query calls the function, loads for a second or two, and then displays no results. Any input, suggestions, or alternative solutions are welcomed.
Here's my code
$that._redisplayBoard = function() {
that._getAndStorePrefData(displayBoard);
this._getItems = function(callback) {
//Build types based on checkbox selections
var queries = [];
queries.push({key:"HierarchicalRequirement",
type: "HierarchicalRequirement",
fetch: "Name,FormattedID,Owner,ObjectID,Rank,PlanEstimate,Children,Ready,Blocked",
order: "Rank"
});
function bucketItems(results) {
var items = [];
rally.forEach(queries, function(query) {
if (results[query.key]) {
rally.forEach(results[query.key], function(item) {
//exclude epic stories since estimates cannot be altered
if ((item._type !== 'HierarchicalRequirement') ||
(item._type === 'HierarchicalRequirement' && item.Children.length === 0)) {
items = items.concat(item);
}
});
}
});
callback(items);
}
rallyDataSource.findAll(queries, bucketItems);
};
function displayBoard() {
artifactTypes = [];
var cardboardConfig = {
types: [],
items: that._getItems,
attribute: kanbanField,
sortAscending: true,
maxCardsPerColumn: 200,
order: "Rank",
cardRenderer: KanbanCardRenderer,
cardOptions: {
showTaskCompletion: showTaskCompletion,
showAgeAfter: showAgeAfter
},
columnRenderer: KanbanColumnRenderer,
columns: columns,
fetch: "Name,FormattedID,Owner,ObjectID,Rank,Ready,Blocked,LastUpdateDate,Tags,State,Priority,StoryType,Children"
};
if (showTaskCompletion) {
cardboardConfig.fetch += ",Tasks";
}
if (hideLastColumnIfReleased) {
cardboardConfig.query = new rally.sdk.util.Query("Release = null").or(kanbanField + " != " + '"' + lastState + '"');
}
if (filterByTagsDropdown && filterByTagsDropdown.getDisplayedValue()) {
cardboardConfig.cardOptions.filterBy = { field: FILTER_FIELD, value: filterByTagsDropdown.getDisplayedValue() };
}
cardboardConfig.types.push("HierarchicalRequirement");
if (cardboard) {
cardboard.destroy();
}
artifactTypes = cardboardConfig.types;
cardboard = new rally.sdk.ui.CardBoard(cardboardConfig, rallyDataSource);
cardboard.addEventListener("preUpdate", that._onBeforeItemUpdated);
cardboard.addEventListener("onDataRetrieved", function(cardboard,args){ console.log(args.items); });
cardboard.display("kanbanBoard");
}
};
that.display = function(element) {
//Build app layout
this._createLayout(element);
//Redisplay the board
this._redisplayBoard();
};
};
Per Charles' hint in Rally Kanban - hiding Epic Stories
Here's how I approached this following Charles' hint for the Rally Catalog Kanban. First, modify the fetch statement inside the cardboardConfig so that it includes the Children collection, thusly:
fetch: "Name,FormattedID,Children,Owner,ObjectID,Rank,Ready,Blocked,LastUpdateDate,Tags,State"
Next, in between this statement:
cardboard.addEventListener("preUpdate", that._onBeforeItemUpdated);
And this statement:
cardboard.display("kanbanBoard");
Add the following event listener and callback:
cardboard.addEventListener("onDataRetrieved",
function(cardboard, args){
// Grab items hash
filteredItems = args.items;
// loop through hash keys (states)
for (var key in filteredItems) {
// Grab the workproducts objects (Stories, defects)
workproducts = filteredItems[key];
// Array to hold filtered results, childless work products
childlessWorkProducts = new Array();
// loop through 'em and filter for the childless
for (i=0;i<workproducts.length;i++) {
thisWorkProduct = workproducts[i];
// Check first if it's a User Story, since Defects don't have children
if (thisWorkProduct._type == "HierarchicalRequirement") {
if (thisWorkProduct.Children.length === 0 ) {
childlessWorkProducts.push(thisWorkProduct);
}
} else {
// If it's a Defect, it has no children so push it
childlessWorkProducts.push(thisWorkProduct);
}
}
filteredItems[key] = childlessWorkProducts;
}
// un-necessary call to cardboard.setItems() was here - removed
}
);
This callback should filter for only leaf-node items.
Mark's answer caused an obscure crash when cardboard.setItems(filteredItems) was called. However, since the filtering code is actually manipulating the actual references, it turns out that setItems() method is actually not needed. I pulled it out, and it now filters properly.
Not sure this is your problem but your cardboard config does not set the 'query' field. The fetch is the type of all data to retrieve if you want to filter it you add a "query:" value to the config object.
Something like :
var cardboardConfig = {
types: ["PortfolioItem", "HierarchicalRequirement", "Feature"],
attribute: dropdownAttribute,
fetch:"Name,FormattedID,Owner,ObjectID,ClassofService",
query : fullQuery,
cardRenderer: PriorityCardRenderer
};
Where fullQuery can be constructed using the the Rally query object. You find it by searching in the SDK. Hope that maybe helps.