PreCanInvoke on Siebel 7.8 (No Script)

It is a little known fact that there is support for a special (undocumented) property that allows one to get rid of the scripting normally used to do PreCanInvoke for methods on applets.

It was recommended in one of our ES reviews, but i do not use it all the time. Why?

Because it dosnt work on every applet, so you still need to script this behaviour on most applets.

Although you do get a special feeling when it does work, knowing that you saved the application some scripting.

For those who want to give it a try, this is the syntax (your mileage will vary):

Property: CanInvokeMethod: <MethodName>
Value: <Expression>

Note: This user property supposedly only works in FrameBase or FrameListBase class applets, but i've found this isnt the case, however this method is documented in Siebel 8.0, so expect it to be more reliable from this version onwards.

Predefault on an Applet (No Script)

There are times when its handy to predefault a field on an applet rather than a BC. This sounds impossible with normal config, there is a way but its really obscure.

Most people would resort to scripting to perform this task, but that path leads to the dark side.

Scenario
In the application, we have the Action BC that is used by normal users to create activites for their day to day work.

There is a new requirement from business, to build a special activites view for a new user group. These people are front line staff, and spend most of their time with the customer so the requirement is to predefault everything for this user base and all they have to fill in are minimal pieces of information.

There are a few ways to meet this requirement.

  1. If users are in a seperate division, then we can use a predefault expression based on their division (dosnt work if users are in one division)
  2. We can get the active view name in a calculated field and predefault based on that (if there are multiple views, then it becomes a very ugly expression)
  3. We can create a brand new BC for this particular group (this would seperate any logic already in Action, and has to be reimplemented in this new BC, and it is extra overhead in the repository)
  4. We can use applet user properties to predefault the field values.

This can be configured as follows:
Property: PostInvokeMethod
Value: NewRecord:SetFieldValue:Contact Id:LoginId()

The trigger is NewRecord, which will fire SetFieldValue on Contact Id field setting the value to LoginId(), the logic is pretty straightforward.

A Siebel eScript framework



In my inaugural post, i'll attempt to tackle, probably one of the most talked about Siebel challenges around.

If you dont know what the ABS (ATI) Framework is, i highly recommend you have a look at the articles on Siebel Unleashed. One of the key features that it provides, is access to a global Class that provides an abstraction layer on top of eScript which allows you to do things such as:

TheApp.Get("Contact").GetFieldValueWithId("12323","Last Name");

This would normally take atleast 10 lines of code to query the BO, BC, get a value from application and add error handling, but the above abstraction does it in 1 line.

In this article, i'll provide a technique to implement a custom object with method chaining, but the million dollar question is how do we build a eScript Framework in Siebel.

Mike M. Lin showed us how to make a custom object available globally in Siebel, his solution was to reference the Application object and attach it to the Object constructor, like below.


Object.prototype.TheApp = this;

What this does is make TheApp available in every script without declaration. At this stage all we've done is made TheApplication() = TheApp.

Mike M. Lin also offers a custom BC method called GetFieldValueUpperCase(), which is applied to the Object constructor, the side effect of attaching custom functions to the Object constructor and calling Siebel methods on non Siebel objects or objects where the method dosnt exist, is you'll get an error.

The GetFieldValueUpperCase() method can be used on BC based objects such as GetBusComp

var oBC=TheApp.ActiveBusObject("Contact").GetBusComp("Contact");
//retrieve recordset
oBC.GetFieldValueUpperCase("Name");

But the same method is also available on every other object in the scripting engine.

var sDummy = "testing";
sDummy.GetFieldValueUpperCase("Name"); //Error

The problem is obvious, the complete solution needs to implement hirerachical method chaining in a class like fashion. This will allow us control what methods are available on what parent object. Since we do not know the Siebel constructors, we will do the next best thing, which is create our own Siebel class, which is what i think the creators of the ABS (ATI) framework have done.

The following code is a protoype that i've created, to emulate the ABS (ATI) Framework.


Object.prototype.TheApp = function () {
return {

Get: function ( sBC ) {

/***********************************************************************/
// Component Name : JLE eScript Class
// Use : TheApp.Get(BC Name)
// .QueryWithSearchExpr(Search Expression)
// .GetFieldValueWithId(Id, Field)
// : TheApp.debug(String)
// Date Author
// 20/02/2009 Jason Le
/************************************************************************/
TheApp.BC = sBC;
return {
QueryWithSearchExpr: function ( sSS ) {
var oBC = TheApplication().ActiveBusObject().GetBusComp( TheApp.BC );
oBC.ClearToQuery();
oBC.SetViewMode( AllView );
oBC.SetSearchExpr( sSS );
oBC.ExecuteQuery( ForwardOnly );
if ( oBC.FirstRecord() )
return oBC;
},
GetFieldValueWithId: function ( sId, sFldName ) {
var oBC = TheApplication().ActiveBusObject().GetBusComp( TheApp.BC );
oBC.ClearToQuery();
oBC.ActivateField( sFldName );
oBC.SetViewMode( AllView );
oBC.SetSearchSpec( "Id", sId );
oBC.ExecuteQuery( ForwardOnly );
if ( oBC.FirstRecord() )
return oBC.GetFieldValue ( sFldName );
}
}
},
debug : function ( text ) {
var fp = Clib.fopen("C:\\debug.txt", "a");
Clib.fputs(text + "\n", fp);
Clib.fclose(fp);
}
}
}();

TheApp.prototype.BC = "";


This class contains 4 methods
Get
Theoretically this would accept upto two parameters, the name of the BO and the BC, if only one parameter is passed in, we use the Active BO.

QueryWithSearchExpr
This method is chained of Get and returns a BC instance with the provided search expression.

GetFieldValueWithId
This method is chained of Get and returns a field value, with the provided row id.

debug
log to file function.

Examples of use.

var BC = TheApp.Get("Contact").QueryWithSearchExpr("[Id]='1-ABCD'");
TheApp.debug ( BC.GetFieldValue("Id") );

var Field = TheApp.Get("Contact").GetFieldValueWithId("1-ABCD", "Name");
TheApp.debug ( Field );


If you look carefully, you notice that the custom object acts as an interface to the Application() methods instead of referencing this on every Object. This allows us to avoid those errors, when we were using the non hierarchical methods.

A lot of complexity has been left out of this code to make it easier to read, it has been hard coded to work on the ActiveBusObject, there is no error handling, there is no consideration into preventing memory leaks, this point is especially important because if improperly implemented, either nothing will work, or when you go into production, it will bring your environment down.

With that disclaimer aside, this idea of creating a custom object structure allows us to be very creative and create our own eScript Framework on top of Siebel.



I know there are architects out there thinking, are you crazy to attach something to the Object constructor? I'll look into a possible concession with the framework, and offer an alternative implementation.

To find out what this means, stay tuned..