Handling numeric ranges in text

I’ve often said in the last six months that the frustrating thing about XPages in comparison to “old Notes” is that sometimes things that were easy before, are now hard. So many of us have 12-18 years of Notes development and can code in LotusScript basically off the top of our heads that doing XPages is almost like starting over.

Such is the case I’ve recently had with handling some text keywords that determine dollar ranges for workflow routing. In the original XPages design, the dollar ranges were in a combo box, for the user to select manually. As such, all four range values were simply put as text values in a multi-value text field in a keyword document. Well, since asking (or trusting) one’s users to select the dollar range seems excessive when we could simply compute it for them, I decided to modify that part of the XPage I’d inherited.

Intially, the four values used in the combo box would return an alias value bound to a field on the document, so the keyword document had these values:

Under $500|1
$500 to $3,000|2
$3,001 to $150,000|3
Over $150,000|4

In my simple world of formula language and LotusScript, I would have been able to use a simple formula that included @TextToNumber, some @RightBack’s and @Left’s to establish what the range border values are. Tossing it into Client Side Javascript (CSJS), it vexed me. So, rather than handle it, I created a range keyword that just had the numeric values and a text keyword that had the nice display text. I promised myself I’d get back to it and in one of the miracles of the modern era, I got back to it within a few weeks (after Connect-o-Sphere both enthused and confused me in the same week).

There’s a computed field used to determine the total value of the purchase order, so I had snuck my code for determining the dollar range right into that formula. This intrigues me because I can have code to recompute the dollar range value only when that total value changes – an option I wouldn’t have had under “old Notes”, where it would have recomputed every time.

I realized that if I’m computing the text to display the dollar range anyway, I could drop the aliasing off the end of the keyword values, which should make them more accessible to my local admins for configuring their workflow. I used Javascript to bind the computed combo box value as follows:

var choices = new TS_Keywords().Lookup("Procurement / Checklist dollar ranges", true);
if (poDoc.isEditable()){
	var number = viewScope.dollarRange; }
else { number = poDoc.getItemValueInteger("DollarRange") };
if ( number > 0 ) {
	number = number-1;
}
return choices[number];

TS_Keywords was written for us by Teamwork Solutions, but basically we’re just getting the multi-value text field with it.

So, to handle that multi-value text field, I needed to use several String methods in Javscript: slice, length, replace and parseInt. I was rather surprised that it both looks like such a complex line of code, but is also relatively simple.

//compute dollarRange and store as a viewScope variable

var number = 4
// compute numeric values of the checklist ranges from the text keyword
var rawlevels = new TS_Keywords().Lookup("Procurement / Checklist dollar ranges", true);
var levels = new Array (rawlevels.length);
for (var i=0;i<rawlevels.length;i++) {
	var space = rawlevels[i].lastIndexOf("$");
	levels[i] = parseInt ( rawlevels[i].slice ( space + 1, rawlevels[i].length ).replace(",","") );
}

// find which range the total falls into
if ( thisVal <= levels[0] ) { number = 1 };
if ( thisVal > levels[0] && thisVal <= levels[1] ) { number = 2 }; 
if ( thisVal > levels[1] && thisVal <= levels[2] ) { number = 3 };
poDoc.setValue("DollarRange",@Integer(number));

While I’m sure this is not the most efficient way to handle this, it avoids the multiple keyword nightmare I was going to foist on those local administrators and might guide someone generally in the direction of solving their own coding problems.

Categories: Client-Side Javascript | Tags: , , , | Leave a comment

Copying views from one database to many

Recently, one of our internal clients asked if we could change who could see a button on one view that is in many of our databases. That sounds pretty easy, right? All you have to do is change the template and those changes will be pushed by the Designer task into every database.

Well, unfortunately, while almost all of the 65 databases in question contain the same view, some of those views don’t link back to any template.

I’d learned a while ago that the old Notes.Net forum was no longer particularly active and that the community had decided to move questions to Stack Overflow.

To digress for a moment, Stack Overflow is part of the Stack Exchange “network of communities” and it’s a great place to both seek and provide answers with experts and budding experts. It was founded in 2010 by one of my favorites in the industry, Joel Spolsky, and Jeff Atwood. Joel’s blog, Joel on Software, provided me with a lot of insights into programming, running a business, dealing with people and, oddly enough, with the first news I had of trouble on the morning of 9/11.

So, if you have a Notes, XPages or other problem, go there and ask. I asked around on Stack Overflow (link) and got a recommendation on how to deal with it. Actually, I got a couple of ideas to choose from and Panu Haaramo’s idea was the best. Basically, get a handle to the view by it’s UNID and copy it to the destination database using XPages.

I put a combobox (fileChoices) on the page to allow the user to select which database to push to, hardcoding in the view name, but it could easily be generalized and looped.

// get a handle to this database
var thisdb:NotesDatabase = session.getCurrentDatabase();
// get server
var server = "MyServer/MyCompany";
// get filepath
var filepath = getComponent("fileChoices").getValue();
getComponent("status").setValue("Searching for "+filepath);
// get a handle to the destination database 
var dbTarget:NotesDatabase = session.getDatabase(server,filepath);
// delete the Manuals view
var oldView:NotesView = dbTarget.getView("Manuals");
if ( @IsNull (oldView) ) { getComponent("status").setValue("Null"); 
} else {
    getComponent("status").setValue(getComponent("status").getValue()+", Trying "+ dbTarget.getTitle());
    try { oldView.remove();
        // get a handle to this Manuals view
        // copy the view to the destination database
        thisdb.getDocumentByUNID(thisdb.getView("Manuals").getUniversalID()).copyToDatabase(dbTarget);
        getComponent("status").setValue(getComponent("status").getValue()+", Updated on "+filepath);
    }
    catch (ex) {
        getComponent("status").setValue(getComponent("status").getValue()+", No update for "+filepath);
    };
}

Any thoughts on how to improve the code (especially the null-checking, which I cannot tell whether it does any good or if the try-catch ends up doing everything) will be greatly appreciated, with credit provided and the code updated.

Somehow, when I posted this internally, it won “Most Popular Newsfeed of the Month” at our office in January. When I tell you I work for an international development company, that seems to make sense… until I point out that the development the rest of the company does is the other kind of development. That is projects designed to help people in developing countries. So, rather than show how well-received my post was internally, it shows that we’re still early on in our efforts at Social Business.

Categories: Utilities | Tags: , , | 3 Comments

Blog at WordPress.com.