Tuesday, June 29, 2010

eScript Framework - Logging Variables

Here is another entry into the logging framework previously discussed. The idea behind this function is to Log multiple variable values to separate lines of our log file using a single line of script. This keeps our script pristine and makes the log file well organized and easy to read. The script to call the function is as follows:

Log.stepVars("Record Found", bFound, " Account Id", sId);
The expected arguments are name/value pairs where the name is a descriptive string (could just be the name of the variable) and the value is the variable itself that we want to track. There is no limit to the number of pairs. There is an optional last parameter to indicate the enterprise logging level (stored in system parameters) above which this line should be written.

The results will be written to the log file as:

06/29/2010 13:33:53 ................Record Found: Y
06/29/2010 13:33:53 ..................Account Id: 1-ADR45
The script to implement follows. This is meant to be added as a new method in the 'eScript Log Framework' business service.

function stepVars () {
var Args = arguments.length;
var iLvl = (Args % 2 == 0 ? 0 : arguments[Args - 1]);
var iParams = (Args % 2 == 0 ? Args : Args - 1;
var sProp, sValue;

for (var i=0; i < iParams; i++) {
sProp = arguments[i++]+": ";
sValue = arguments[i];
Log.step(sProp.lPad(30, ".")+sValue, iLvl);
}
}
Also, a new line will need to be added to the Init section to instantiate this function on Application start:

Log.prototype.stepVars = stepVars;
I want to draw particular attention to two java features which may be useful in other applications. The first is how to reference a variable number of arguments in a function call. Notice the special array variable, 'arguments'. This array is defined as all of the arguments passed to this function with no special declarations. It can be referenced just like any other array. There are some exceptions with how this array can be manipulated though with push() and pop() not working as you might expect.

The second, is how to assign a variable using an inline if, ( condition ? value if true : value if false). The condition is any expression that will evaluate to either true or false. The first expression after the ? is the value returned if the condition evaluates to true, and the last expression is what is returned if the consition evaluates to false.

Thursday, June 24, 2010

Activity Plans vs SMART Templates

I wrote a post recently about a new service offering I have been working on to greatly speed up process automation. So this is another entry into my teaser series. Let me start by describing what is probably Siebel's first and most basic attempt of process automation: the Activity Plan. Activity Plans have been around in Siebel going back a long time. I remember them in 2000 and they may have been there in 99.5 though to be honest I don't recall exactly when they made their appearance. Basically, an administrator creates an Activity Template consisting of a series of Activities. A user can then either automatically trigger the creation of an instance of this template (an Activity Plan) from an Opportunity Sales Stage transition or manually add one to any other object. Once the Plan is added, the Activities are automatically generated. This sounds great to a lot of business stakeholders as it sounds like something they can apply in many scenarios. It's strengths are:
  • Can set any/all fields on an activity
  • Creates many activities at once, saving manual effort.
Unfortuneately, once you start gathering any sort of requirements for a business process, you will start to stumble across the weaknesses:
  • Fields can only be set to constant values (this really impacts dates when it comes to activities)
  • Activities are created all at once, so any type of sequencing is impossible
  • This functionality only exists for Activities (No Service Requests, or other custom objects)
Now with customization, there are ways to get around some of these limitations, but at some point, you will probably end up either building something completely different or bastardizing the Activity BC itself.

In my last post on this topic I introduced you to the RARE Engine (Rule-based Approvals Routing and Escalations). This is really two parts. I already touched on its features. There is actually a second component of my automation suite which I have branded SMART Templates. What a SMART Template does is to create a task record and set fields on that record, while addressing all of the deficiencies of the Activity Plan:
  • Can create/update records of any type (administrator specifies the BC)
  • Can evaluate fairly complex expressions including date math to set fields
And when used in combination with the RARE Engine:
  • Records can be created in batches at different points in time, dependent on the completion of prior tasks.
OK, now we are getting somewhere. So once this service offering is implemented, any process can be maintained through the Siebel UI. You need to change the threshold at which a VP needs to approve an Order? No problem. You need to notify an additional person at a point in the New Customer Onboarding process? Ok. Or you need to create three new Service Requests when Final Contract approval is given? You got it. You want to update the quote status when the customer approves it through your eSales portal? You betcha. All these things can be done by an administrator in Real Time.

Just remember, most processes are just a series of steps executed by people or systems. What the RARE/SMART Suite provides is a way to implement automation quickly, maintain those steps in the Siebel GUI, and enrich the processes themselves (Reporting, Reliability, Refinement).

Monday, June 21, 2010

SR Class Level Read Only

In case anyone has ever wondered about the intricacies of how a Service Request BC record becomes read only when the Status is set to Closed, I hope to add a bit of enlightenment. The premise, is that in Vanilla Siebel, if I set the Status of an SR to 'Closed', the entire record becomes read only. There are many tickets about this on My Oracle support so this may not be new territory. Instead of implementing this through standard configuration, which seems easily poossible in modern versions, Siebel has done this at the class level, in CSSBCServiceRequest. The interesting thing for me in researching this is what the actual triggering point is that makes the record read only. Basically, Siebel compares the value of the Status field for the particular record against the Display Value of the LOV where Type = 'SR_STATUS' and Name (Language Independent Code) = 'Closed'.

So if for instance the Display value has been changed in the LOV table to Done for the 'Closed' LOV record in type 'SR_STATUS', then the record must have a value of 'Done' in the status in order for this record to be read only. Maybe this result is not very interesting. So far you are right. What is interesting is that if you remove the picklist altogether from the Status field, or change it to a different picklist using a different LOV_TYPE, the same evaluation occurs. The long and short of it is that there is not a lot of opportunity to customize this functionality without doing some more complicated things behind the scenes as the Class does a lot of hard coded checks in order to implement this requirement

Friday, June 18, 2010

A Process Automation offering

I have discussed before that I think business process automation is the ultimate end state of a CRM Architect. What I mean by that, is that the ultimate way CRM pays off and that we as solution architects make value for our clients is to make real business processes that occur in our client organizations, and design mechanisms for the client to perform them in the CRM solution such that they execute faster, with more reliability, and to be measurable. In this case, the CRM solution is Siebel.

So my own work/life balance finds me pursuing three parallel threads on the work side (We won't get too much into the life side in this blog). First are my client responsibilities. I think all my readers have similar ones, so I won't go into them. Second, is this blog. My goal for this blog has always been a technical repository. That is, the solutions I post about here are geared towards technical Siebel professionals and can be leveraged against any functional design. My thoughts on why an open source approach for this basically comes down to the fact that any technical expert who sees this logic can and will take it for themselves. I mean that in the best way possible. What makes a technical expert truly valuable, is not only to understand new concepts, but to have a repository of all the things they have seen and done before. While, I think applying the algorithms I post about can return tremendous value to a client, I do not think these types of algorithms can be proprietary in the sense of the profitable. You would spend your life fending off those who copy and modify. I think the value to ourselves comes from showing our clients that we can understand these concepts and apply them. For me, writing a blog about them is a way to demonstrate that and I encourage other long term Siebel experts to do the same.

The third aspect of my professional pursuits I have not posted about before, but it is related directly to what I believe should be all of our prime objective when it comes to CRM, process automation. Basically, I have built another alternative to Siebel Workflow/Smartscripts/Task UI which has a different set of strengths for what I feel was missing in the Siebel base product. It is a task automation engine. The idea is that business processes are typically a series of steps or tasks assigned to different people. So I built a framework in which an administrator can setup and maintain these tasks and who they are assigned to in Siebel. The seed of my idea came from the out of the box Siebel Approvals functionality, and the Universal Inbox. Vanilla approvals can assign inbox items to a series of named employees or positions, and it is implemented in the Campaigns and Quotes modules off the top of my head. The approval thread can be either linear or parallel. This is interesting but it is immediately apparent that it is very limited. After all, a well implemented client may have spent a lot of time implementing complex assignment rules to assign objects to people based on all sorts of rules. I may want to assign approvals to people dynamically based on these same assignments. Or perhaps I want to approve dynamically up a position hierarchy. Or maybe what I need to automate is not technically an approval at all, but a series of service requests. Or maybe I want to assign an item to a different person based on attributes of the item being evaluated, say a dollar amount field.

What I have built hopes to address all of these functional requirements and much more into a product offering that can be administered 100% from the GUI and specifically adds value by addressing the three pillars of process automation:
  1. faster execution - Escalation of items after designated time periods, scheduled reminders, Just in time notification to avoid "notification overload"
  2. with more reliability - System controls the next assignee, rule based, version controlled rule matrices
  3. and to be measurable - see current process status, steps stored in DB, can report on overall process metrics (avg length, bottlenecks, etc)
Getting back to my earlier point about what should be open source and what not. I have decided that this engine, which includes many functional algorithms will remain proprietary. I think the approach I have come up with involves some innovations which are not easily reproducible. I hope to use this blog as one mechanism to communicate my expertise in the area of process automation. I appreciate any good will and contacts I receive through this blog so feel free to let me know if something like this may be of value to your clients/employers. I will continue to post information about this engine in Teaser format.

Tuesday, June 15, 2010

Why can't I see that View???

There are times when troubleshooting an issue, that you start searching Oracle support and you find a ticket that has your problem, but the solution does not apply, and then you keep looking and you find another one that looked in a different direction yet still, the solution did not apply. And you think, I wish there was a single place that listed all the reasons that this could happen. Siebel does do this sometimes a handful of their generic error messages, listing ten different reasons you could be getting that message. Well I am going to apply that format to some basic configuration items.

The first one is View Visibility. So without further ado, here is a list of reasons you may not see a particular view in the GUI (Please feel free to add additional reasons in comments):
  1. The basics: View has been created in Tools, compiled into the SRF, and the GUI you are looking at matches the SRF you compiled the view into. I know this part should be obvious, but we are aiming at completeness
  2. The View needs to be added to a screen. Make sure the View attribute spelling matches the spelling of the View object. Check the Display In Page unless you want this to be hidden, and probably the Display In Site Map. These default to True.
  3. The View exists in the GUI meta data. Administration - Application -> Views. Again make sure the spelling matches the repository object
  4. The View has been added to a responsibility that your user login has access to
  5. The Responsibility Cache has been cleared. Doh!
  6. Log Out and Log Back In (Views are not immediately visible in the session where it was added)

Ok those are the basics. Now for the advanced:

  1. Navigate to the Application - Administration -> Responsibilities -> Tab Layout view. Check the responsibility which is primary for your user login in the top applet. Query for the Application you are logged into in the middle applet. Find the Screen you have placed the view under in the third applet, and in the fourth applet, insure the Hide flag is not checked. This is more applicable for trying to figure out why a vanilla view is not exposed
  2. Navigate to the Application - Personalization -> Views view. Query for the view which is you are having issues with. Review the Condition Expression. This expression should either be null, or should evaluate to true for the logged in user. The date range should either be null or should include today's date in its range.
  3. Last but not least, it may be a license key issue. Siebel implement license keys through their views. One way to test whether this is the issue*** is to copy the view, add the copied view to a responsibility, add the copied view to the View admin and to a responsibility and clear the cache, log out and back in. If you can see the view now, then you need to get the license key. Not all license keys indicate additional purchases. There are a couple of instances I have found where a view just dropped out of the standard set as a defect and the fix was to provide a license key to get it back (Remote System Preferences view is an example I ran into in 8.o)

Some Additional Pointers:

  • Always copy and paste view names between objects or between Tools and the UI to avoid spelling errors, as they are one of the most common problems in this area.
  • Avoid the use of apostrophes to indicate possesive as this will often cause issues down the road. (I know siebel has some vanilla instances where theu use it with ...Manager's... but trust me, avoid this).
  • When copying views, insure the Thread and Visibility applets are actually present as View Web Template Items for that view. No error is thown to the UI if they do not match, but buried in the Siebel.log, you will find them. They manifest by not executing the correct search when navigating across views. For instance if you are on an All view and navigate to the correlated My view, but one that has an invalid applet, the view will appear to change in the UI, but the data does not, so the My view will show All view data.
*** This should be done just to test that the license key is the issue. A copied view really should not go into production as this is a slippery slope which is really not a good idea in the long term (and its against the license agreement too)

UPDATE: Dos, in comments, points out a better way to see if Licensing (or some other problem) is at issue. Just paste the following into your URL after the start.swe? replacing the view name in bold with the view you are having problems with:

SWECmd=GotoView&SWEView=Quote+List+View

You need to replace a space with a +. Since 'Quote List View' is part of the Orders module requiring a specific license key, you get the following message if you do not have the key:

View 'Quote List View' is not licensed for this site.(SBL-DAT-00327)

Thursday, June 10, 2010

The Framework - Revised

After some fits and starts, I finally got around to a data dump of my Siebel eScript Framework with some rough instructions on how to implement it. After a very worthwhile back and forth on Jason Le's LinkedIn group I have some structural modifications to make. The new framework will be implemented as a pair of business services. The main advantage of this is the code is more centrally located in a single repository. Multiple applications can reference it there. A fairly good case has been made that the logic can all sit in a single business service underneath one object, TheApplication. I think there are decent reasons to do either but preferences may vary.

Create a new BS, called 'eScript Framework' and check the Cache flag.
It's PreInvoke should have the following:


try {
var bReturn = ContinueOperation;
switch (MethodName) {
case "Init":
bReturn = CancelOperation;
break;
}
return (bReturn);
}
catch(e) {
throw(e);
}


Then create a method for each function in the framework from the previous post. So far the Methods I have are:
AddToDate
DateToString
StringToDate
DiffDays
GetLocalTime
GetSysPref
SetSysPref
QueryExprArrayReturn

Now, create the Logging BS. Create a new Business Service named, 'eScript Log Framework', and check the Cache flag. Its PreInvoke should have the following:


try {
var bReturn = ContinueOperation;
switch (MethodName) {
case "Init":
var sPath = Frame.GetSysPref("Framework Log Path");
sPath = sPath.replace(/\\$/, ""); //Remove trailing backslash if used
gsOutPutFileName = sPath+"\\Trace-"+
TheApp.LoginName()+"-"+
Frame.GetLocalTime("%02d%02d%d%02d%02d%02d")+".txt";

//Get the System Preference Log Level. Get the Log Level set for this user (if provided)
//and then set the log level for this session
var sLogLevel = Frame.GetSysPref("CurrentLogLevel");
if (TheApp.GetProfileAttr("User Log Level") != "")
TheApp.SetProfileAttr("CurrentLogLevel", TheApp.GetProfileAttr("User Log Level"));
else TheApp.SetProfileAttr("CurrentLogLevel", sLogLevel);
Log.step("Session Logging Level: "+TheApp.GetProfileAttr("CurrentLogLevel"), 1);
bReturn = CancelOperation;
break;
}
return (bReturn);
}
catch(e) {
throw(e);
}


Set the Declarations section to the following:


var gsOutPutFileName;
var giIndent = 2; //Indent child prop sets this many spaces for each level down.
var giPSDepth = 0; // How deep in the property set tree, what levelvar CurrentLogLevel = 2;
var gaFunctionStack = new Array(); //used in debugStack function to store called functions
var giStackIndex = 0; //Where in the function stack the current function resides
var gsIndent = ''; //used in debug methods to identify stack indents
var giLogBuffer = Frame.GetSysPref("Log Buffer");
var giLogLines = 0;
var gsLogCache = "";


Then create a method for each function in the framework from the previous post. So far the Methods I have are:
step
StartStack
Stack
Unstack
RaiseError
PropSet
DumpBuffer

Now open up the Server script for the Application object you are using (this should be done in every Application being used where framework functions may be referenced). Add this to the Declarations section:


Object.prototype.TheApp = this;
Object.prototype.Frame = TheApp.GetService("eScript Framework");
Object.prototype.Log = TheApp.GetService("eScript Log Framework");

Frame.InvokeMethod("Init", NewPropertySet(), NewPropertySet());
Log.InvokeMethod("Init", NewPropertySet(), NewPropertySet());


Your done. Log and Frame functions can now be referenced from anywhere in your scripts.