I'm running into problems getting aging information for a customer record via a Restlet. Specficially I'm looking to get the aging, aging1, aging2, aging3, and aging4 fields from the Customer form for a given customer.
In the UI those values are found on the Customer form under the "Financial" section and look something like:
Current 1-30 Days 31-60 Days 61-90 Days Over 90 Days
1200.00 800.00 720.37 423.23 42.00
My Restlet code looks something like this:
…
cols[6] = new nlobjSearchColumn('aging');
var result = nlapiSearchRecord(data.record_type, null, filter, cols);
return result;
It works great with other fields such as "balance", but when I include the "aging" field and run my GET I see this error:
"error": {
"code": "SSS_INVALID_SRCH_COL",
"message": "An nlobjSearchColumn contains an invalid column, or is not in proper syntax: aging."
}
Clearly I'm not doing something right. Are those fields special in some way? How do I retrieve those values?
As far as I recall there is no search column for customers called 'aging'. This is the cause for the Invalid Search column error.
Also those fields might not be exposed either to the searches or suitescript which is why you are getting the error.
I actually contacted Netsuite and the previous comments are correct, those fields are not exposed, so I simply can't use them.
There is currently an enhancement request #238854 to expose these fields.
Easy enough to add in a suitescript context. e.g.
var aging = nlapiSearchRecord('invoice', null, [
new nlobjSearchFilter('daysoverdue', null, 'greaterthan', 0),
new nlobjSearchFilter('mainline', null, 'is', 'T'),
new nlobjSearchFilter('amountremaining', null, 'greaterthan', 0)
//for your RESTLet maybe you need this: , new nlobjSearchFilter('entity', null, 'is', data.customer_id)
], [
new nlobjSearchColumn('entity', null, 'group'),
new nlobjSearchColumn('formulanumeric', null, 'sum').setFormula('case when {daysoverdue} < 31 then {amountremaining} else 0 end'),
new nlobjSearchColumn('formulanumeric', null, 'sum').setFormula('case when {daysoverdue} between 31 and 60 then {amountremaining} else 0 end'),
new nlobjSearchColumn('formulanumeric', null, 'sum').setFormula('case when {daysoverdue} between 61 and 90 then {amountremaining} else 0 end'),
new nlobjSearchColumn('formulanumeric', null, 'sum').setFormula('case when {daysoverdue} > 90 then {amountremaining} else 0 end')
]);
aging.forEach(function (inv) {
var cols = inv.getAllColumns();
console.log(inv.getText('entity', null, 'group') +
' 0-30: ' + inv.getValue(cols[1]) +
' 31-60: ' + inv.getValue(cols[2]) +
' 61-90: ' + inv.getValue(cols[3]) +
' > 90: ' + inv.getValue(cols[4]));
});
Just a followup note on this: After almost three years Netsuite has still not acted on that enhancement request even though it has been voted for 30 times. Also the solution above results in numbers that are usually, but not always, in line with what is displayed by Netsuite.
Related
I am executing nlapiSearchRecord function on itemfulfillment record and the search is working fine but its return result with repetition/duplicate.
Below is the my working code i just want to know that which filter should i use to avoid repetition/duplicate record.
var filters = [
new nlobjSearchFilter('internalid', null, 'is',id),
new nlobjSearchFilter('mainline', null, 'is', 'F'),
new nlobjSearchFilter('shipping', null, 'is', 'F'),
new nlobjSearchFilter('taxline', null, 'is', 'F')
], columns = [
new nlobjSearchColumn('trandate'),
new nlobjSearchColumn('tranid'),
new nlobjSearchColumn('item'),
new nlobjSearchColumn('quantity'),
new nlobjSearchColumn('location')
];
var searchresults = lapiSearchRecord('itemfulfillment',null, filters, columns);
Below is the search result for your understanding and you can see that there is one item but it's is duplicated two times with positive quantity and one time with negative quantity.
[{"id":"123","recordtype":"itemfulfillment",
"columns":{"trandate":"2/1/2222","tranid":"xx-xxxx","status":{"name":"xxxx","internalid":"xxxx"},"item":{"name":"Test-1","internalid":"1111"},"quantity":1,"location":{"name":"xxxx","internalid":"xxx"}}},
{"id":"123","recordtype":"itemfulfillment",
"columns":{"trandate":"2/1/2222","tranid":"xx-xxxx","status":{"name":"xxxx","internalid":"xxxx"},"item":{"name":"Test-1","internalid":"1111"},"quantity":1,"location":{"name":"xxxx","internalid":"xxx"}}},{"id":"123","recordtype":"itemfulfillment",
"columns":{"trandate":"2/1/2222","tranid":"xx-xxxx","status":{"name":"xxxx","internalid":"xxxx"},"item":{"name":"Test-1","internalid":"xxxx"},"quantity":-1,"location":{"name":"xxxx","internalid":"xx"}}},{"trandate":"2/1/2222","tranid":"xx-xxxx","status":{"name":"xxxx","internalid":"xxxx"},"item":{"name":"XXXXX","internalid":"1111"},"quantity":1,"location":{"name":"xxxx","internalid":"xxx"}}},
{"id":"123","recordtype":"itemfulfillment",
"columns":{"trandate":"2/1/2222","tranid":"xx-xxxx","status":{"name":"xxx","internalid":"xxxx"},"item":{"name":"Test-2","internalid":"xyz"},"quantity":2,"location":{"name":"xxx","internalid":"xxxx"}}},{"id":"123","recordtype":"itemfulfillment","columns":{"trandate":"2/1/2222","tranid":"xx-xxxx","status":{"name":"xxxx","internalid":"xxxx"},"item":{"name":"Test-2","internalid":"xyz"},"quantity":2,"location":{"name":"xxxx","internalid":"10"}}},{"id":"123","recordtype":"itemfulfillment",
"columns":{"trandate":"2/1/2222","tranid":"xx-xxxx","status":{"name":"xxxx","internalid":"xxxx"},"item":{"name":"Test-2","internalid":"xyz"},"quantity":-2,"location":{"name":"xxxx","internalid":"xxxx"}}}]
Can you please guide me that how can i get items without any repetition/duplication.
The positive and negative quantities relate to the G/L entries created by the fulfillment.
Choose the G/L account for the side of the transaction you are interested in as another filter.
I am very new to NoSQL. My usecase is related to this.... Many users post messages and we store it in cloudant as different documents
{
id:random,
userid:xxx,
timestamp: 1449216912282,
msg: "Hi..."
}
I want to find out users who have not posted anything for last 5 days - Additionally I want to know if they have posted anything between last five and 10 days. If they have, then send a reminder mail to user to be active.
Which option would be better - views, search, cloudant query? Assume we will be having 1000s of posts per hour
I though of creating view - map(userid,timestamp) reduce as _stats and get max timestamp of each user. Then iterating through this list - we get users who did not post in last 5 days.
Other option was to use search index, get all userids between required timestamps. Compare both lists in application.
Is there any way to do it in a single query without overloading the application? Would Changing data format or creating appropriate index or view help?
If your data looked like this:
{
"_id": "abcdefghijklmon1234",
"userid" : "user1",
"timestamp": 1449739485035,
"msg": "Hi"
}
You could create a MapReduce view that created an index with a key consisting of [ 2015, 50, "user1" ], where "2015" is the year, "50" is the week number and "user1" is the document's userid. This can be achieved with a Map function like this:
function (doc) {
var getWeek = function(t) {
var date = new Date(t);
date.setHours(0, 0, 0, 0);
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
var week1 = new Date(date.getFullYear(), 0, 4);
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7);
};
if (typeof doc.timestamp == "number") {
var d = new Date(doc.timestamp);
var weeknum = getWeek(d.getTime());
var year = d.getFullYear();
emit( [ year, weeknum, doc.userid], null);
}
}
with a reduce of "_count". This allows queries such as ?startkey=[2015,49]&endkey=[2015,50]&group_level=3 to get a list of users who DID post last week. The list of users who didn't are the users who don't appear in the above list.
This isn't a solution to your problem in terms of "in the last 5 days", but uses week numbers instead.
This is related to a question I asked previously:
How to get customer aging fields from a Netsuite restlet
The technique described in the answer to that question works great, but it doesn't handle credit memos. I'm having problems figuring out how to take credit memos into account.
For example, I have a customer record that displays the following values:
BALANCE
1950.00
OVERDUE BALANCE
2000.00
CURRENT 1-30 DAYS 31-60 DAYS 61-90 DAYS OVER 90 DAYS
0.00 -50.00 2,000.00 0.00 0.00
I can pull the 2000 out just fine, but I can't seem to get the -50 from the credit memo.
I tried adjusting the invoice query to do this:
new nlobjSearchFilter('amountremaining', null, 'notequalto', 0),
I also tried doing a separate query for credit memos:
var agingcmemo = nlapiSearchRecord(
'creditmemo',
null,
[
new nlobjSearchFilter('daysoverdue', null, 'greaterthan', 0),
new nlobjSearchFilter('mainline', null, 'is', 'T'),
new nlobjSearchFilter('amountremaining', null, 'notequalto', 0),
new nlobjSearchFilter('entity', null, 'is', result[0].id)
],
[
new nlobjSearchColumn('entity', null, 'group'),
new nlobjSearchColumn('formulanumeric', null, 'sum').setFormula('case when {daysoverdue} < 31 then {amountremaining} else 0 end'),
new nlobjSearchColumn('formulanumeric', null, 'sum').setFormula('case when {daysoverdue} between 31 and 60 then {amountremaining} else 0 end'),
new nlobjSearchColumn('formulanumeric', null, 'sum').setFormula('case when {daysoverdue} between 61 and 90 then {amountremaining} else 0 end'),
new nlobjSearchColumn('formulanumeric', null, 'sum').setFormula('case when {daysoverdue} > 90 then {amountremaining} else 0 end')
]
);
That query returns no rows. If I remove all conditions except for the entity it returns two rows, neither of which is the actual credit memo.
Anyone have any ideas on how to get those right numbers including the credit memos?
there's no concept of daysoverdue with a credit memo. If you run the code below does that sum to $50? You should be able to use that to get your open credit memo value and combine that with your open invoice amounts.
The other than that is it possible that you have an unapplied payment or customer deposit?
var agingcmemo = nlapiSearchRecord(
'creditmemo',
null,
[
new nlobjSearchFilter('mainline', null, 'is', 'T'),
new nlobjSearchFilter('status', null, 'anyof', ['CustCred:A']),
new nlobjSearchFilter('entity', null, 'is', 996)
],
[
new nlobjSearchColumn('entity', null, 'group'),
new nlobjSearchColumn('amountremaining', null, 'sum')
]
);
agingcmemo.forEach(function(c){
var cols = c.getAllColumns();
cols.forEach(function(col, idx){ console.log(idx +': '+c.getValue(col));});
console.log('');
});
I've used a different approach to building an aging report in a Saved Search that might work better for you as it takes credit memos into account as well. (The groups below age by months rather than 30 day increments but you can replace them with the formula you were using before):
You can define "Transaction Type" in the search criteria, this way you can create saved searches for each specific transaction types. Alternately you can add transaction type as a filter even color code it by the transaction types. In my opinion transaction search is the mother of all searches in NS because majority of objects and fields are exposed to this search type.
I am in need of getting the text (display) value of a field to compare date ranges on the posting period. I have the formula below I am pushing in to a filter array with other filters I need to run
var columns = [];
columns.push(new nlobjSearchColumn('transactionnumber'));
columns.push(new nlobjSearchColumn('line'));
columns.push(new nlobjSearchColumn('item'));
columns.push(new nlobjSearchColumn('account'));
columns.push(new nlobjSearchColumn('amount'));
columns.push(new nlobjSearchColumn('vsoeallocation'));
columns.push(new nlobjSearchColumn('postingperiod'));
columns.push(new nlobjSearchColumn('srctranpostperiod','revrecschedule'));
columns.push(new nlobjSearchColumn('recuramount','revrecschedule'));
columns.push(new nlobjSearchColumn('jedoc', 'revrecschedule'));
columns[0].setSort();
columns[1].setSort();
var filter = [];
var formula = "case when TO_DATE({postingperiod.displayname}, 'MON YYYY') >= " + newselectedDate.getFullYear() + " then 1 else 0 end";
filter.push(new nlobjSearchFilter('revrecenddate', null, 'after', newselectedDate));
filter.push(new nlobjSearchFilter('type', null, 'anyof', ['CustInvc', 'CashSale']));
filter.push(new nlobjSearchFilter('templatename','revrecschedule','isnotempty'));
filter.push(new nlobjSearchFilter('deferredamount','revrecschedule','isnot', 0));
filter.push(new nlobjSearchFilter('formulanumeric', null, 'equalto', 1).setFormula(formula));
var search = nlapiCreateSearch('transaction', filter, columns);
var searchresult = search.runSearch();
Which in theory would work except that it gets the value of that field instead of the text (it turns out to be a non date value). I know there are ways once you have ran a search to get the text instead of the value (nlobjSearchResult.getText) but I have not found a way to do this within a formula.
Any ideas?
It seems that posting period might actually contain additional information that is apparently only used in functions like TO_DATE(). This may be because of the internal hierarchy of accounting periods. Or it might be because periods are List/Record fields in a single select box, so there is an ID for every posting period. To get around this, I've done the following (applied to your code):
var formula = "case when TO_DATE(SUBSTR({postingperiod},-8,8), 'MON YYYY') >= " + newselectedDate.getFullYear() + " then 1 else 0 end";
Which is basically adding a SUBSTR function to pick up the last 8 characters in the string to pass onto the date function.
Im using the datatables plugin for pagination.
I'm trying to get my server-side pagination working, however to start with, im facing the issue of not been able to display the totalRecords entires in the 'Showing 1 to 15 of 15 entries' text. im using this to display all the records:
var resultsTable = tableEl.dataTable( {
"aaData": tableData,
"aoColumns": tableColumns,
"aaSorting": [[1,'asc']],
// "bServerSide": true,
"processing": true,
"serverSide": true,
"sAjaxDataProp": "api/1.0/accounts",
"bProcessing": true,
"pagingType": "simple_numbers",
"bLengthChange": false,
"bAutoWidth": false,
"bScrollCollapse": true,
"iTotalRecords": 34,
"iTotalDisplayRecords":15,
"iDisplayLength": 15
} );
but it still shows me 'Showing 1 to 15 of 15 entries' instead of 'Showing 1 to 15 of 34 entries'. Any idea what im i missing here?Thanks, in advance!
My understanding is that iTotalRecords and iTotalDisplayRecords are values that are returned from the server, not values that you set during initialisation.
Can you add the code that generates the server-side data? How are you actually paging that source data?
In your accounts method, you should be returning a json object that looks something like this:
return Json(new
{
param.sEcho,
iTotalRecords = totalRecords,
iTotalDisplayRecords = totalFilteredRecords,
aaData = result
}, JsonRequestBehavior.AllowGet);
Where iTotalRecords is the total, unfiltered record count and iTotalDisplayRecords is the filtered record count. result contains 15 rows of data because at this point you will have already performed the paging query using the datatable parameters iDisplayStart and iDisplayLength to generate the data. You now have all the information to correctly display the page when you return this json object to the datatable.