Welcome to the OPA Hub!


Tag Archives: List of Values

Custom Options : Hierarchical Lists

Custom Options : Hierarchical Lists

Following on from the previous post, where we looked at how you might use an Entity and it’s instances to provide the dynamic input for a List of Values at run-time, this post will investigate how we might go one step further and implement a hierarchical lists with two dynamic lists based on instances.

Going back to something I mentioned in the previous post, I’ve generally been mystified why Hierarchical Lists have always been a struggle in Oracle Policy Modeling. I don’t think that exporting a List, modifying the XML file and then re-importing it is exactly business friendly. I know and appreciate that Stephen Estes created a cool Excel tool to make it more friendly by hiding all the XML manipulation from the naked eye, but the overall process just seems, well, lame to me.

In the previous post, we looked at building a Custom Options extension to pull values from an Entity. That at least means that instances become the run-time source of the values. But what about having two value lists in a hierarchy, and having both of them based on Entity Instances? Basically, create an Excel spreadsheet for your dynamic values using inferred instances, and read that into the hierarchy dynamically?

As soon as you sit down and look at this, some thorny problems jump out. Most obviously the following:

  1. The Custom Options extension does not provide any keys for our code like the ones you see in Inputs and elsewhere : updatevalidatemount or unmount. So the concept of “update the child because the parent changed” becomes harder.
  2. The Entities (assuming for the moment that there are two) will be arranged in a hierarchy, which means a double iteration – find the parents, then find the children for each parent. This would be time consuming.

From a component perspective, we will have two Controls, both Custom Options. Two JavaScript files. They will need to have a few Custom Properties on the Controls, to support the concept (such as the Property “EntityName” which we will pass into the JavaScript. But the child list will need to be able to access the Parent value.

In the end, for the purposes of demonstration, we are going to do this with three files. Two Custom Options and a Custom Input. The Custom Input does not do much, except provide us with a hook to hang a line of code, so that when the parent value is updated we can refresh the child values.

Our basic spreadsheet looks like this:

OPA 12 - Custom Options Hierarchy Data Source

Regular readers will recognize this as a data set we have used previously in posts about RuleScript. This time however we will just use it as source data for our Custom Options hierarchy. There are two Entities, the station and the child station. Each parent has multiple children. You get the idea. Pick a station, then view and pick a child station. The entities and attributes have been set up with names to be able to reference them in our JavaScript.

  1. These attributes have nothing to do with the example, they are just to fill out the page
  2. The parent and child entities are added with Entity Containers. There is a Boolean attribute to ensure that they are hidden, but they need to be present for the data to be available to JavaScript
  3. These are the attributes involved in the Hierarchy
  4. This Control has a Custom Option extension. It is the parent list.
  5. This Control has a Custom Input extension. It is never entered manually, and simply shows the user what they selected. A decent CSS would make it either invisible or at least a bit nicer to look at.
  6. This Control has a Custom Option extension. It is the child list.

First the parent list, which is identical to the example shown in the previous post. It simply reads the instances into the Options list.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
OraclePolicyAutomation.AddExtension({
	customOptions: function (control, interview) {
		if (control.getProperty("name") == "xOptionsParent") {
			console.log("Custom Options Extension found " + control.getProperty("name"));
				var entityId = control.getProperty("entityname");
				var entities = interview._session.config.data;
				var entityinstance;
				var entity;
				var myStations = []
				for (i = 0; i < entities.length; i++) {
					entity = entities[i];
					if (entity.entityId === entityId) {
						break;
					}
				}
				var Stations = entity.instances;
				for (i = 0; i < Stations.length; i++) {
 
					myObject = new Object();
					for (j = 0; j < Stations[i].attributes.length; j++) {
						entityinstance = Stations[i].attributes[j];
 
						if (entityinstance.attributeId === "station") {
							textofentry = entityinstance.value
						}
 
					}
 
					myObject.text = textofentry.toString();
					myObject.value = textofentry.toString();
					myStations.push(myObject);
				}
 
				console.log("List of stations now ready" + JSON.stringify(myStations));
 
				return {
					options: myStations,
					controlType: "Dropdown"
				}
 
		}
	}
});

The child list Custom Options file is very similar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
 * Richard Napier The OPA Hub Website January 2019
 * Educational Example of Custom Options List as Hierarchical LOVs
 * I will remember this is for demonstration purposes only
 */
OraclePolicyAutomation.AddExtension({
	customOptions: function (control, interview) {
		if (control.getProperty("name") == "xOptionsChild") {
			//console.log("Custom Options Extension found " + control.getProperty("name"));
			var myStation = interview.getValue("parentstation");
				var entityId = control.getProperty("childentityname");
				var entities = interview._session.config.data;
				var entityinstance;
				var entity;
				var myChildStations = []
				for (i = 0; i < entities.length; i++) {
					entity = entities[i];
					if (entity.entityId === entityId) {
						break;
					}
				}
				var Stations = entity.instances;
				var shortList = Stations.filter(function (el) {
						return el.parentId.attrVal == myStation;
					});
				for (i = 0; i < shortList.length; i++) {
 
					myObject = new Object();
					for (j = 0; j < shortList[i].attributes.length; j++) {
						entityinstance = shortList[i].attributes[j];
 
						if (entityinstance.attributeId === "child_station") {
							textofentry = entityinstance.value
						}
 
					}
 
					myObject.text = textofentry.toString();
					myObject.value = textofentry.toString();
					myChildStations.push(myObject);
				}
 
			//	console.log("List of child stations now ready" + JSON.stringify(myChildStations));
				return {
					options: myChildStations,
					controlType: "Dropdown"
				}
		}
	}
});

Lines 22 -25 try and optimize the process of grabbing the child stations by using a direct access to the child stations in JavaScript and then filtering the array to only get those whose parent id is the correct one. This seemed faster than iterating over all the children first.

So far so good. The two options will work fine, if you place them on different screens, with the Parent before the child. But we need them to be on the same Screen. The third JavaScript file, the Custom Input, looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/*  Generated by the OPA Hub Website 23/01/2019 15:22
Educational Example of Custom Input Extension for Oracle Policy Automation
 I will remember this is for demonstration purposes only. 
*/
OraclePolicyAutomation.AddExtension({
    customInput: function(control, interview) {
        if (control.getProperty("name") == "xInput") {
            return {
                mount: function(el) {
                    //console.log("Starting customInput Mount " + control.getProperty("name"));
					var div1 = document.createElement("divinput")
                    var div2 = document.createElement("input");
                    div1.id = "myContainer";
					div2.id = "myInput";
                    div2.value = control.getValue();
					div1.appendChild(div2);
                    el.appendChild(div1);
                    //console.log("Ending customInput Mount");
                },
                update: function(el) {
					var now = new Date();
					//console.log("Starting customInput Update " + control.getProperty("name") + " " + now);
                    //control.setValue(interview.getValue("parentstation"));
                    //console.log("Ending customInput Update " + now + " " + interview.getValue("parentstation"));
					//console.log("Control " + control.getValue());
					if(interview.getValue("parentstation") != null && interview.getValue("parentstation") != "" && interview.getValue("parentstation") != control.getValue()){
						control.setValue(interview.getValue("parentstation"));
						//console.log("Updating Now");
					interview.saveData();
					}
                },
                validate: function(el) {
                    console.log("Starting customInput Validate");
                        control.setValue(document.getElementById("myInput").value);
						//interview.setInputValue("parentstation", document.getElementById("myContainer").value)
						//console.log(document.getElementById("myInput").value);
						//interview.update();
		                return true;
                    console.log("Ending customInput Validate");
                },
                unmount: function(el) {
                    if (control.getProperty("name") == "xInput") {
                       // console.log("Starting customInput UnMount");
                        var myContainer = document.getElementById("myInput");
                        myContainer.parentNode.removeChild(myContainer);
                       // console.log("Ending customInput UnMount");
                    }
                }
            }
        }
    }
})

The only lines of code that matter are the update. Basically whenever the parent station is not null, and has a different value to this custom input, then save the interview. This triggers a refresh, and the parent and child lists are redrawn, and the new values are available to the user.

It’s not pretty, but short of hacking some real DOM events we couldn’t find a way to update the list since (as we mentioned earlier) it does not have any keys like update or validate that we might use.

In the end the user experience is reasonably cool, with the lists being refreshed:

Hierarchical Lists Animation

Hierarchical Lists Animation
If you are interested in the finished Project just drop a comment and I will happily share it, with absolutely no warranty.

List of Values, Display Values and Translations in Oracle Policy Automation

List of Values, Display Values and Translations  in Oracle Policy Automation

A common List of Values question that comes up is the following:

“How are list of values handled in Oracle Policy Automation, when I have more than one language? What gets put into the database?”

Of course, by “database” I mean whatever platform you are using to save the response from Oracle Policy Automation  : Oracle Sales Cloud, Oracle Service Cloud, Oracle Siebel CRM, Salesforce or any other.  For the record, the exact behaviour of your Connector will depend on how it has been programmed,but if it is doing what it should be doing, then the following information will apply.

The question that comes up is actually, to be fair, usually more detailed : people want to know the behaviour of the list of values both when it is created as a Screen element (single-use) or as a Value List (multi-use) and when there is more than one language involved. Well, fear not because the video below will answer all of those questions I hope, in a friendly and easy to follow way.

Imagine you have a Project in Oracle Policy Automation that includes an attribute of type Text, with a drop-down list to display values for the user.

  • What if there are multiple languages?
  • What if you use Screen-based Values?
  • What if you add Display Values as well as Values?
  • What if you migrate to Project Value Lists?
  • What editing might you have to do in your Translation file(s)?
  • What gets into the database?

List of ValuesAll these points will be covered in the video. Hopefully there will be no surprises in the video itself,  but it will be useful to see it in action and get confirmation of what you think might happen. And as mentioned previously, a big thanks to our OPA Hub Website Sponsors Mantis Solutions, makers of the OPA DB Connector, whose assistance made this video way easier to create and much faster to implement. If you are interested in Mantis OPA DB Connector, please reach out here.

 

Until the next post, I hope you are (at least those of you in the Northern Hemisphere) all having a pleasant Summer.

OPA – Entities Adventures #2 Question Screens…

Back to the Workshop

So we return to our Rulebase with Mechanics and Cars. Eagle-eyed readers will see this is an adapted version of an exercise in the Policy Automation training course. The version we are using here has different relationships and attributes than the one in the course. I am not intending to copy the material used in the course.

Next Steps

In the previous post we set up our Entities and experimented wth some Containment Relationship text. Now we can begin to study our Entiities in more detail and create / edit Attributes to extend the model.

Fire up the Debugger using Build and Debug without Screens. Let’s look at the output in the Debugger. Go ahead and right-click the “all the cars” icon and choose Add Instance.

OPA - Entities Create Instance

Since the car currently has only one Attribute, you don’t have much choice as to what to enter – just double-click the green bar and type something like “Ford Fiesta”.

OPA - Create Car Instance

 

Which brings us to the question – just what is this information we are entering? Where did it come from, and what issues may we have?

The Attriibute you are entering was created automatically when you created the Entity definition. It is the “identifying attribute”. If this was the Mechanic, maybe it would be his or her name, or employee Id. Since it is the car, what would it be? I’ll hasard a guess and say why not the car registration plate, since it is unique and a useful way to distinguish between all those cars in the workshop. Of course our Car Entity will have a Make and Model as well, no doubt. So let us correct all of this and spot a couple of other traps along the way.

Back out of the creation and Click Build > Stop Debugging. Now double-click the Attriibute and let us review. We want this to represent the Car Registration. So go ahead and change the text. And observe :

OPA - Car Registration Identifying Attribute

Common trap #4. The Car is not a he or a she (despite what some drivers thiink!). It is impersonal – an “It” if you will. Failure to update the Default Gender will of course make for strange output in the Decision Report and Debugger.

OPA - Car Gender Error

 

Take the opportunity to give the Attribute a public name as well. Now we have spoken about the Car Entity, what about the Mechanic – for the Mechanic there is another one to think about.

Common trap #5 The Mechanic needs to be defined as male or female (depending on your business scenario, this may not be useful for our Rulebase, but there are many situations where it is!). To set up the “Gender Attribute” is quite simple. We have covered it before so just visit this post and resume when the Mechanic has a Gender Attriibute defined. Your Mechanic Entity should now have 2 Attributes and should look like this.

OPA - Mechanic Gender

Note that we have also edited the Attribute Text for the Identifying Attriibute since the Mechanic’s name, in our case, will be enough to differentiate between them.

The Gender Attribute will come in very handy later on in the Web Determination and Decision Report for our Rulebase, Let’s now extend our Rulebase by adding a second rule to our Word Document. Make sure you create the Attribute as a Number and that you create it in the Properties Files (that could even be common trap #5b). The creation of the Attribute will look like this:

OPA - Attribute Creation in Word Document

And the Rules should look like this now.

OPA - All the Mechanics

Now that job is done, we can begin to investigate the Gender Attribute. Firstly, we need to create a Screen File in our Project, and Create a Question Screen inside it.

OPA - Mechanic Basic Question Screen

Check your own project against the screenshot. If you are seeing “Who is the mechanic’s name?” then you need to edit the Attribute to “impersonal”, or if you chose to keep the original Attribute Text “the mechanic” then you will see that instead. Notice the new icon to show the Gender Attribute.

Build and run your Web Determination, and click the link referring to Mechanics. Add a Mechanic using the Add New Instance button.

Ignore any warnings you might get at this stage since our Interview is not really ready yet, so it has no real goals.

OPA - Create Mechanic Screen Basic

Excellent – Policy Automation has given us the two radio buttons and has shown it understands this is a Gender Attribute.

Common trap #6 Asssuming that this is going to work in different languages.

As you may know, in this demonstration sequence we have also been maintaining a parallel version in French.

OPA - French Version of Project

Having entered the different Entities, Attributes, Rules in Word and having now created the Question Screen in the Screens File, this is what we get when we run the French Web Determination.

OPA - French Mechanic Screen Basic

I’m sure you can spot the issue. To correct this we could go to the Question Screen, and the Gender Attribute Question, and change the Display Values for the List of Values.

OPA - French Gender List of Values

 

And so finally we get good output when we Build and Run our Web Determination.

OPA - French Mechanic Screen Basic Improved

This editing of the Display Values may come in handy of course even if you are not working in a different language. The solution to the “male/ female” issue can be found in Part Six if you can wait that long.

In the next post in this series we will get our Reference Relationships up and running. Until next time!