Save document in a for loop using computeWithForm - xpages

I have the following button that can save multiple values in a for loop using computeWithForm
//to keep the code simple, I use hardcode value to save document except values in for loop
var forloopvalues = #DbLookup(#DbName(),"CompetencyCourseViewCourseFirst", "myFieldValue1",2 );//use myFieldValue1 to lookup in the view
var date ="13/03/2017";
for(var i =0;i<forloopvalues.length;i++)
{
var mdoc = database.createDocument();
cdate = session.createDateTime(date);
mdoc.replaceItemValue("Form", "myForm");
mdoc.replaceItemValue("myField1","myFieldValue1")
mdoc.replaceItemValue("myField2", forloopvalues[i]); //suppose there are four values in the forloopvalues
mdoc.replaceItemValue("myField3","myFieldValue3");
mdoc.replaceItemValue("myField4", "myFieldValue4");
mdoc.replaceItemValue("myField5", "myFieldValue5");
if(mdoc.computeWithForm(false,false))
{
mdoc.save(true, true);
getComponent("computedField1").setValue("Record saved");
}
else
{
}
}
When I click the button, it can save the document. In the Lotus Notes Client, I can see there are four documents in the view. All the computed fields in the form also filled. However, I notice myField2 only saves the first value in forloopvalues not all values. So although there are four documents, both of them save the first value only in myField2. Therefore, if the computed field in the form that contains myField2 plus other values, it only show the first value in myField2 plus other values
I review the code, there is forloopvalues[i] within the for loop and I don't understand why it only saves the first value only.
For example, assume forloopvalues are valueA, valueB, valueC, valueD. The myField2 only save valueA four times. If there is a computed field in the form that connect myField2 with other values together, the computed field value show valueA+othervalues four times.
I have another attempt, if I use document.save() + On document save in Run form validation in Data tab
In Lotus Notes Client to open the view and read the saved document. It will not show computed field in the form but the loop value is correct
For example, assume forloopvalues are valueA, valueB, valueC, valueD. The myField2 save valueA, valueB, valueC, valueD for each document. If there is a computed field in the form that connect myField2 with other values together, the computed field value show each forloopvalues+othervalues four times (e.g. valueA+othervalues, valueB+othervalues, valueC+othervalues, valueD+othervalues)
I believe computeWithForm is almost near the result although it save the first value in forloopvalues only. In Help Contents, I search about computeWithForm. It's syntax is
computeWithForm(dodatatypes:boolean, raiseerror:boolean) : boolean
and compare to the code in the application I think the syntax is fine. I double check the for loop and not understand why it only save the first value.
So how can I use computeWithForm to save the loop value. Thanks a lot.
update
I think computeWithForm is almost near the result because it can fill all the computed field in the form, but it saves one value in multiple times depends on the number of forloopvalues.
If forloopvalues has one value only, then it saves one time. It forloopvalues has three values, it save three times, each document (in myField2) contains the value of forloopvalues.
If I use document.save(), it can save documents for each value in forloopvalues individually but it will not fill the computed field in the form.
update
I try the following code to see if the value appears correctly inside the function
var forloopvalues = #DbLookup(#DbName(), "CompetencyCourseViewCourseFirst", "myFieldValue1", 2);
for (var index in forloopvalues) {
if (forloopvalues.hasOwnProperty(index)) {
makeAnewDoc(forloopvalues[index]);
}
}
function makeAnewDoc(field2Value) {
var mdoc = database.createDocument();
cdate = session.createDateTime(date);
mdoc.replaceItemValue("Form", "myForm");
mdoc.replaceItemValue("myField1", "myFieldValue1")
mdoc.replaceItemValue("myField2", field2Value);
if (!viewScope.resultMessage) {
viewScope.resultMessage = [];
}
if (mdoc.computeWithForm(false, false)) {
mdoc.save(true, true);
viewScope.resultMessage.push("Record saved " + forloopvalues[index]);
} else {
viewScope.resultMessage.push("Save failed for " + forloopvalues[index]);
}
// Bleeding memory otherwise!
mdoc.recycle();
}
The result is when I click the button, the screen can show this message (suppose forloopvalues has four values)
Record saved valueA ,Record saved valueB ,Record saved valueC ,Record saved valueD
Due to the screen can show the forloopvalues, I feel strange why it only saves the same value four times.

Your code actually would save the last value. Don't loop through the values and save the same document 4 times. ReplaceItemValue can take an array as input. So you can use your lookup result directly. No loop required. Should be faster too
Did misread your question on tiny mobile screen. You need to change your loop slightly:
var forloopvalues = #DbLookup(#DbName(), "CompetencyCourseViewCourseFirst", "myFieldValue1", 2);
var mdoc; // In SSJS (ES3 there are no block level variables!)
for (var index in forloopvalues) {
if (forloopvalues.hasOwnProperty(index)) {
mdoc = database.createDocument();
cdate = session.createDateTime(date);
mdoc.replaceItemValue("Form", "myForm");
mdoc.replaceItemValue("myField1", "myFieldValue1")
mdoc.replaceItemValue("myField2", forloopvalues[index]);
// Bind your UI field to a scope variable
// don't access them directly
if (!viewScope.resultMessage) {
viewScope.resultMessage = [];
}
if (mdoc.computeWithForm(false, false)) {
mdoc.save(true, true);
viewScope.resultMessage.push("Record saved " + index);
} else {
viewScope.resultMessage.push("Save failed for " + index);
}
// Bleeding memory otherwise!
mdoc.recycle();
}
}
or better: pull the function to save the document out (variables are function level in SSJS, not block level):
var forloopvalues = #DbLookup(#DbName(), "CompetencyCourseViewCourseFirst", "myFieldValue1", 2);
for (var index in forloopvalues) {
if (forloopvalues.hasOwnProperty(index)) {
makeAnewDoc(forloopvalues[index]);
}
}
function makeAnewDoc(field2Value) {
var mdoc = database.createDocument();
cdate = session.createDateTime(date);
mdoc.replaceItemValue("Form", "myForm");
mdoc.replaceItemValue("myField1", "myFieldValue1")
mdoc.replaceItemValue("myField2", field2Value);
if (!viewScope.resultMessage) {
viewScope.resultMessage = [];
}
if (mdoc.computeWithForm(false, false)) {
mdoc.save(true, true);
viewScope.resultMessage.push("Record saved " + index);
} else {
viewScope.resultMessage.push("Save failed for " + index);
}
// Bleeding memory otherwise!
mdoc.recycle();
}
Let us know how it goes.

ComputeWithForm broke at some point in the Domino 9.0.1 fix pack cycle. Not sure which FP broke it. IBM has confirmed this issue and is looking at a fix. The onload, onsave, and both options do not work. SPR #LHEYAKALAH

Related

trying to extract rowIds from smartsheet using node.js

I am trying to iterate through and existing smartsheet to get the rowIds (which I need for setting parent/child relationships).
I am struggling :-(
Obviously I don't get the required syntax on this one. Here is what I tried:
var options = {
Id: *******************
};
var row_ids = '';
// get the sheet
smartsheet.sheets.getSheet(options)
.then(function(sheetinfo) {
// iterate through the rows array and build comma-delimited list of row ids
for (rowIds in sheetinfo) {
row_ids += row_ids.concat(', ');
}
});
console.log(row_ids);
Any help would be most appreciated.
Bowow99
In the Get Sheet response, sheetInfo contains a collection of rows and each row object in that collection contains the property id. So to successfully implement the scenario you've described, you need to loop through the sheetInfo.rows array, and for each row in that array, append the value of its id property (followed by a comma) to create the comma-delimited string that you're attempting to build. At the end of this loop, there'll be an extra comma at the end (following the last row id you added to the string) -- so you'll need to remove it using the substring function.
The following code does what I've described above. Please note, I'm not a JavaScript expert -- so there may be a more efficient way to do this...but this code gets you the end result you're after: a comma-delimited string of the row Ids within the specified sheet.
var options = {
id: 8337220210845572 // Id of Sheet
};
var row_ids = '';
// Get sheet
smartsheet.sheets.getSheet(options)
.then(function(sheetInfo) {
// iterate through rows in the sheet and build comma-delimited string of row ids
for (var i=0; i < sheetInfo.rows.length; i++) {
row_ids += sheetInfo.rows[i].id + ',';
}
// remove the final (excess) comma from the end of the row_ids string
row_ids = row_ids.substring(0, row_ids.length-1);
console.log(row_ids);
})
.catch(function(error) {
console.log(error);
});

Angular - TypeError Cannot set _id to Null Value

Has a transaction function that worked in first pass and at 2nd pass, got "TypeError: Cannot set _id to Null value. Both passes were to create a new transaction. Besides, system seemed to indicate that there was value for variable that was being used to assign to_id. In this case,
print"this.selectedLeave_id" and saw value as expected. However, Angular right away in next statement complaining that Null value was set to "this.SLTran.leave_id".
Below pls find code and any help is appreciated.
onSubmitLeaveTran()
{
if (this.selectedLeaveTran_id != 0) // means first loaded all trans and then select from table to modify
{
this.sLTran = this.tempSLTran; // tempSLTran was from row selected to be modified
this.sLTran.leaveType = this.tempLeaveType; // from dialog box edited data
this.sLTran.leaveStartDate = this.tempLeaveStartDate; // from dialog box edited data
this.sLTran.leaveEndDate = this.tempLeaveEndDate; // from dialog box edited data
this.sLTran.numDaysRequested = this.tempNumDaysRequested; // from dialog box edited data
console.log('2-2.5 inside onSubmit Leave Tran for update :',this.sLTran);
this.staffLeaveDataSvc.updateSLTran(this.sLTran).subscribe((sLTran:staffLeaveTran) => {this.sLTran = sLTran});
}
else
{ // a new tran
console.log('2-2.4 inside onSubmit Leave Tran selectedLeave_id for new tran:',this.selectedLeave_id);
this.sLTran.leave_id = this.selectedLeave_id; // first established the leave_id associated with this new tran
this.sLTran.leaveType = this.tempLeaveType;
this.sLTran.leaveStartDate = this.tempLeaveStartDate;
this.sLTran.leaveEndDate = this.tempLeaveEndDate;
this.sLTran.numDaysRequested = this.tempNumDaysRequested;
this.staffLeaveDataSvc.addSLTran(this.sLTran).subscribe(
(sLTran:staffLeaveTran) => {
this.sLTran = sLTran
});
}
};

Netsuite Userevent Script

I have a userevent script to change the Field in Contract record from PO record. The Script is running fine. But whenever I edit a contract record and try to submit it : It throws the error "Another user has updated this record since you began editing it. Please close the record and open it again to make your changes".
May I know the reason behind this ?
/*---------------------------------------------------------------------------------------------------------------
Description : Whenever the PO vendor is changed(due to Split vendor) that should replace the same in Contract page record automatically.
Script type : User Event Script
Script id : customscript452
Version : 1.0
Applied to : Contract
----------------------------------------------------------------------------------------------------------------*/
function srchfield()
{
var stRecordid = nlapiGetRecordId(); //returns the contract id
if(stRecordid== undefined || stRecordid== null || stRecordid==' ')
{
}
else
{
var stRecordtype = nlapiGetRecordType(); //returns the contract record type = jobs
var stRecord = nlapiLoadRecord(nlapiGetRecordType(), stRecordid);
nlapiLogExecution('debug','Load Object',stRecord);
var stContractID = stRecord.getFieldValue('entityid'); //returns the value of the field contractid whose fieldid is = entityid
nlapiLogExecution('debug','stContractID',stContractID);
var stCompanyName = stRecord.getFieldValue('companyname'); //returns the value of the field company name whose fieldid is = companyname
nlapiLogExecution('debug','stCompanyName',stCompanyName);
var stConcatenate = stContractID+" : "+stCompanyName; //Concatenate the two Fields to get the result which needs to be found in PO
var arrFilters = new Array(); // This is Array Filters all the Purchase Order Record Search
arrFilters.push(new nlobjSearchFilter('type', null, 'anyof',
[
'PurchOrd'
]));
arrFilters.push(new nlobjSearchFilter('mainline', null, 'is', 'T')); //This is to exclude line level results
arrFilters.push(new nlobjSearchFilter('custbodycontract', null, 'is', stRecordid)); //This is Filters in Contracts Search
var arrColumns = new Array();
arrColumns.push(new nlobjSearchColumn('entity')); //This is Search Column Field in Records
var arrSearchresults = nlapiSearchRecord('purchaseorder', null, arrFilters, arrColumns); //This is Filters in Search Result Purchase Order
if(arrSearchresults== undefined || arrSearchresults== null || arrSearchresults==' ')
{
}
else
{
var length = arrSearchresults.length;
}
if(length== undefined || length== null || length==' ')
{
}
else
{
for (var i = 0; arrSearchresults != null && i < arrSearchresults.length; i++)
{
var objResult = arrSearchresults[i];
var stRecId = objResult.getId();
var stRecType = objResult.getRecordType();
var stCntrctName = objResult.getValue('entity'); //This is Value are Get Purchase Order Records and Field for Vendor = entity
}
}
//var record = nlapiLoadRecord(nlapiGetRecordType(), stRecordid, stCntrctName);
if (stCntrctName =='custentityranking_vendor_name')
{
}
else
{
var stChangeName = stRecord.setFieldValue('custentityranking_vendor_name', stCntrctName); //This is Value are the Set in Main Vendor Field = custentityranking_vendor_name
nlapiSubmitRecord(stRecord, null, null); // Submit the Field Value in Record Type
}
}
}
The User Event script executes as the Contract record is being saved to the database. At the same time, you are loading a second copy of the record from the database and trying to submit the copy as well. This is causing the error you're seeing.
You fix this by just using nlapiSetFieldValue to set the appropriate field on the Contract.
I might also recommend getting more familiar with JavaScript by going through the JavaScript Guide over at MDN. In particular, take a look at the Boolean description so that you know how JavaScript evaluates Boolean expressions. This will help you greatly reduce the amount of code you've written here, as many of your conditionals are unnecessary.
What userevent do you have? It is happening depending on what type of user event and API you are using. Looking at your code, you are trying to load contract record that is already updated at the database. So you might consider below to address your issue. Hope, it helps.
If it is a before submit, you don't need to load the record where the script is deployed.
Just use nlapiGet* and nlapiSet* to get and set values. You also don't need to use nlapiSubmitRecord to reflect the change. With before submit, it executes before the record is being saved to the database. So your changes will still be reflected.
Then if it is after submit, it will be executed after the record has been saved to the database, Thus you might use the following API depending on your needs. Actually, this is the best practice to make sure the solution .
nlapiGetNewRecord - only use this if the script only needs to retrieve info from header and sublists. And nothing to set.
nlapiLookupField - use this if the script only needs to get value/s at the header and nothing from the line.
nlapiSubmitField - the script don't need to load and submit record if the changes only on header. Just use this API.
nlapiLoadRecord and nlapiSubmitRecord- use the former if the script will have changes at the line and then use the latter api to commit it on the database.
Being a user event script code, The code you showed is very not good considering performance.
Here is the sample you can merge
var stRecordid = nlapiGetRecordId(); //returns the contract id
// Every record has an internal id associated with it. No need to add condition explicitly to check if its null
var stRecordtype = nlapiGetRecordType();
var fields = ['entityid','companyname'];
var columns = nlapiLookupField(stRecordtype, stRecordid, fields);
var stContractID = columns.entityid;
var stCompanyName = columns.companyname;
nlapiLogExecution('debug','stContractID/stCompanyName',stContractID+'/'+stCompanyName);
var stConcatenate = stContractID+" : "+stCompanyName; //Concatenate the two Fields to get the result which needs to be found in PO
//
//your code of search
//you can improve that code also by using nlapilook up
nlapiSubmitField(stRecordtype, stRecordid, 'custentityranking_vendor_name', 'name to be updated');

How can I transform a notes view to a html nested list?

I would like to re-use a notes view in a web browser, Therefor I need the notes view (with response documents hierarchy) represented in HTML as an unordered list (ul) with list items (li).
What SSJS code should I use to compute this list?
None.
If you can edit the view, set it to passthru HTML and add one column at the beginning and end with the list tags. Set them hidden from client.
Or bind it to a repeat control and have the Li tags inside with computed text bound to the view columns. No SsJS in both cases.
NotesViewEntry.getPosition(Char separator) gives a hierarchical output. For example with the separator defined as "." it will give 3 for the third top-level entry, 3.5 for the fifth child of the third top-level entry, 3.5.7 for the seventh child of the fifth child of the third top-level entry.
To elaborate on Stephan's second option, a Repeat Control doesn't care about the structure of the data it's retrieving. It's a handle to a collection, where each "row" is one element in that collection. So if you point it to a collection which is myView.getAllEntries(), each entry is a NotesViewEntry.
Combine the two and you have the level of the hierarchy, if you want to just use indentation. Alternatively, from a NotesViewEntry you can tell if there are children, so whether you need to make it another li or start another ul.
Alternatively, if you want to get more elaborate, look at how I traverse views to create a Dojo Tree Grid navigation in XPages Help Application http://www.openntf.org/internal/home.nsf/project.xsp?action=openDocument&name=XPages%20Help%20Application
not the most beautiful code. I hope it works;
function getList() {
var nav:NotesViewNavigator=database.getView("notesview").createViewNav();
var entry:NotesViewEntry=nav.getFirst();
if (entry!=null){
var countLevel:Integer = 0;
var curLevel:Integer;
var list="";
while (entry != null) {
var edoc:NotesDocument = entry.getDocument();
entryValue = entry.getColumnValues().elementAt(1).toString();
var col:NotesDocumentCollection = edoc.getResponses();
var gotResponse:String;
if (col.getCount()>0){
gotResponse ="1";
}
else{
gotResponse ="0";
}
curLevel = entry.getColumnIndentLevel();
if (curLevel<countLevel){
//no responses & no siblings
var difLevel=countLevel-curLevel;
list=list + "<li>"+entryValue+ "</li>"
var closure="";
for (var i=0;i<(difLevel);i++) {
closure=closure+"</ul></li>"
}
list=list+closure;
countLevel=curLevel;
}
if (curLevel==countLevel){
if(gotResponse=="1"){
//got responses;handle them first
list=list+"<li>";
list=list+entryValue;
list=list+"<ul>";
countLevel=curLevel+1;
}
else{
//must be sibling
list=list + "<li>"+entryValue+ "</li>"
}
}
var tmpentry:NotesViewEntry=nav.getNext(entry);
entry.recycle();
entry=tmpentry;
}
//final closure, last entry could be response doc
var closure = ""
for (var i = 0; i < (countLevel); i++) {
closure = closure + "</ul></li>";
}
list = list + closure;
return list;
} else {
return "No documents found";
}
}

Select top/latest 10 in couchdb?

How would I execute a query equivalent to "select top 10" in couch db?
For example I have a "schema" like so:
title body modified
and I want to select the last 10 modified documents.
As an added bonus if anyone can come up with a way to do the same only per category. So for:
title category body modified
return a list of latest 10 documents in each category.
I am just wondering if such a query is possible in couchdb.
To get the first 10 documents from your db you can use the limit query option.
E.g. calling
http://localhost:5984/yourdb/_design/design_doc/_view/view_name?limit=10
You get the first 10 documents.
View rows are sorted by the key; adding descending=true in the querystring will reverse their order. You can also emit only the documents you are interested using again the querystring to select the keys you are interested.
So in your view you write your map function like:
function(doc) {
emit([doc.category, doc.modified], doc);
}
And you query it like this:
http://localhost:5984/yourdb/_design/design_doc/_view/view_name?startkey=["youcategory"]&endkey=["youcategory", date_in_the_future]&limit=10&descending=true
here is what you need to do.
Map function
function(doc)
{
if (doc.category)
{
emit(['category', doc.category], doc.modified);
}
}
then you need a list function that groups them, you might be temped to abuse a reduce and do this, but it will probably throw errors because of not reducing fast enough with large sets of data.
function(head, req)
{
% this sort function assumes that modifed is a number
% and it sorts in descending order
function sortCategory(a,b) { b.value - a.value; }
var categories = {};
var category;
var id;
var row;
while (row = getRow())
{
if (!categories[row.key[0]])
{
categories[row.key[0]] = [];
}
categories[row.key[0]].push(row);
}
for (var cat in categories)
{
categories[cat].sort(sortCategory);
categories[cat] = categories[cat].slice(0,10);
}
send(toJSON(categories));
}
you can get all categories top 10 now with
http://localhost:5984/database/_design/doc/_list/top_ten/by_categories
and get the docs with
http://localhost:5984/database/_design/doc/_list/top_ten/by_categories?include_docs=true
now you can query this with a multiple range POST and limit which categories
curl -X POST http://localhost:5984/database/_design/doc/_list/top_ten/by_categories -d '{"keys":[["category1"],["category2",["category3"]]}'
you could also not hard code the 10 and pass the number in through the req variable.
Here is some more View/List trickery.
slight correction. it was not sorting untill I added the "return" keyword in your sortCategory function. It should be like this:
function sortCategory(a,b) { return b.value - a.value; }

Resources