Try OpenEdge Now
skip to main content
Application Developer's Guide
Developing Balanced Scorecard applications : Balanced scorecard infopads : Defining rule file syntax for balanced scorecard infopads : Rule file syntax for modifying each scorecard table
 
Rule file syntax for modifying each scorecard table
The Finance infopad is an example of how to modify rules for each scorecard table. What applies to the Finance infopad here also applies to the Customer, Innovation, and Internal infopads.

Syntax

In the following sample, the rule file syntax is presented in code formatted text, immediately followed by the rule explanation.
rule Finance_WorstValue activated by EVT_1 of
BPEVENT_INFOPAD_ALARM::worstcheckFinance
This rule covers updates to the WorstValue slots in the finance infopad. When those values are modified, the alarm condition, worstcheckFinance, is triggered and sends an event. This rule is activated by that event.
then
{Finance[EVT_1.row][EVT_1.column].Score :=
(((((Finance[EVT_1.row][EVT_1.column].CurrentValue -
Finance[EVT_1.row][EVT_1.column].WorstValue)/
(Finance[EVT_1.row][EVT_1.column].BestValue -
Finance[EVT_1.row][EVT_1.column].WorstValue))*10)/
Finance[EVT_1.row]["FinanceScore"].RelativeWeight));
This updates the KPI score of the cell at the row and column where the alarm was triggered, the context.row and context.column provide us with these values.
The current, worst, and best value sub-equation gives the percentage that the current value is between worst value and best value, between 0 and 1. Multiplying by 10 gives us the range of the score from 0 to 10. Dividing by the sum of the relative weights normalizes the score when the sum is greater than one. If the score equals one, then it is inconsequential. This prevents user error from breaking the calculations.
discard(EVT_1);
}
This command destroys the event.
rule Finance_BestValue activated by EVT_1 of
BPEVENT_INFOPAD_ALARM::bestcheckFinance
then
{Finance[EVT_1.row][EVT_1.column].Score :=
(((((Finance[EVT_1.row][EVT_1.column].CurrentValue -
Finance[EVT_1.row][EVT_1.column].WorstValue)/
(Finance[EVT_1.row][EVT_1.column].BestValue -
Finance[EVT_1.row][EVT_1.column].WorstValue))*10)/
Finance[EVT_1.row]["FinanceScore"].RelativeWeight));
    discard(EVT_1);
}
This rule does the same action as the worstcheck rule, only it is triggered by updates to the BestValue slots in the FinanceInfopad. When that occurs, it updates the score for that KPI. See the description of the previous rule for more information.
rule Finance_CurrentValue_GT activated by EVT_1 of
BPEVENT_INFOPAD_ALARM::currentcheckFinance
    if (Finance[EVT_1.row][EVT_1.column].NegativeRange = "F")
This rule is triggered by the alarm event that occurs when the current value slot is updated and the range of worst to best value is positive. The negative range is false, which is positive.
then
{Finance[EVT_1.row][EVT_1.column].WorstValue ::=
min(toFloat(EVT_1.slotvalue));
If the new current value is less than the worst value, set the worst value to be equivalent to the current value, so that the out-of-range values do not spoil the calculations. Values out of the worst and best values need not necessarily be between 1 and 10. This spoils the subsequent scores based on that erroneous calculation.
Finance[EVT_1.row][EVT_1.column].BestValue ::=
max(toFloat(EVT_1.slotvalue));
If the new current value is greater than the best value, set the best value to be equivalent to the current value.
generate(type : "BPEVENT_INFOPAD_ALARM", value :
"updateFinanceScore", slot : "4", row : EVT_1.row, column :
EVT_1.column, table : "", NegativeRange : "");
    discard(EVT_1);
}
This instruction generates an event of the exact same type with the value equal to UpdateFinanceScore, which triggers a rule to update the finance score in this cell.
rule Finance_CurrentValue_LT activated by EVT_1 of
BPEVENT_INFOPAD_ALARM::currentcheckFinance
    if (Finance[EVT_1.row][EVT_1.column].NegativeRange = "T")
This rule is triggered when the current value slot is updated and the range of worst to best value is negative and the event is sent by the alarm condition you have set up. The negative range is true.
then
{Finance[EVT_1.row][EVT_1.column].WorstValue ::=
max(toFloat(EVT_1.slotvalue));
If the new current value is greater than the worst value, set the worst value to be equivalent to the current value.
Finance[EVT_1.row][EVT_1.column].BestValue ::=
min(toFloat(EVT_1.slotvalue));
If the new current value is less than the best value, set the best value to be equivalent to the current value.
generate(type : "BPEVENT_INFOPAD_ALARM", value :
"updateFinanceScore", slot : "4", row : EVT_1.row, column :
EVT_1.column, table : "", NegativeRange : "");
    discard(EVT_1);
}
This instruction generates an event of the exact same type with the value equal to UpdateFinanceScore, which triggers a rule to update the finance score in this cell.
rule Finance_CurrentValue_update activated by EVT_1 of
BPEVENT_INFOPAD_ALARM::updateFinanceScore
This event updates the finance score of the cell which originated the event and triggers this event only when it is generated from an alarm condition.
then
{Finance[EVT_1.row][EVT_1.column].Score :=
(((((Finance[EVT_1.row][EVT_1.column].CurrentValue -
Finance[EVT_1.row][EVT_1.column].WorstValue)/
(Finance[EVT_1.row][EVT_1.column].BestValue -
Finance[EVT_1.row][EVT_1.column].WorstValue))*10)/
Finance[EVT_1.row]["FinanceScore"].RelativeWeight));
    discard(EVT_1);
}
The current, worst, and best value sub-equation gives the percentage that the current value is between worst value and best value between 0 and 1. Multiplying by 10 gives you the range of the score from 0 to 10. Dividing by the sum of the relative weights normalizes the score when the sum is greater than one. If the score equals one, then it is inconsequential. This prevents user error from breaking the calculations.
rule Finance_Weight activated by EVT_1 of
BPEVENT_INFOPAD_ALARM::relativecheckFinance
This event updates the overall relative weight when one of the individual KPI relative weights change. This also tends to renormalize all of the scores in the infopad because they depend on the overall relative weight which changed.
if (EVT_1.column != "7")
It is not equal to the overall relative weight slot from the last column which is FinanceScore or 7.
then
{Finance[EVT_1.row]["FinanceScore"].RelativeWeight :=
(Finance[EVT_1.row][1].RelativeWeight +
Finance[EVT_1.row][2].RelativeWeight +
Finance[EVT_1.row][3].RelativeWeight +
Finance[EVT_1.row][4].RelativeWeight +
Finance[EVT_1.row][5].RelativeWeight +
Finance[EVT_1.row][6].RelativeWeight);
This illustrates the overall relative weight slot is the sum of the other 6 relative weights reflected here in the update call.
generate(type: "BPEVENT_INFOPAD_ALARM",value :
"updateFinanceScore",slot : "4",row : EVT_1.row, column : 1, table :
"", NegativeRange : "");
generate(type: "BPEVENT_INFOPAD_ALARM",value :
"updateFinanceScore",slot : "4",row : EVT_1.row, column : 2, table :
"", NegativeRange : "");
generate(type: "BPEVENT_INFOPAD_ALARM",value :
"updateFinanceScore",slot : "4",row : EVT_1.row, column : 3, table :
"", NegativeRange : "");
generate(type: "BPEVENT_INFOPAD_ALARM",value :
"updateFinanceScore",slot : "4",row : EVT_1.row, column : 4, table :
"", NegativeRange : "");
generate(type: "BPEVENT_INFOPAD_ALARM",value :
"updateFinanceScore",slot : "4",row : EVT_1.row, column : 5, table :
"", NegativeRange : "");
generate(type: "BPEVENT_INFOPAD_ALARM",value :
"updateFinanceScore",slot : "4",row : EVT_1.row, column : 6, table :
"", NegativeRange : "");
    discard(EVT_1);
}
You can renormalize all of the scores as the overall relative weight has changed.
rule Finance_Score activated by EVT_1 of
BPEVENT_INFOPAD_ALARM::scorecheckFinance
This rule updates the perspective scores when one of the individual KPI scores has changed.
if (EVT_1.column != "7")
...and it was not the overall perspective score in column FinanceScore or seven.
then
{Finance[EVT_1.row]["FinanceScore"].Score :=
((Finance[EVT_1.row][1].Score*Finance[EVT_1.row][1].RelativeWeight) +
(Finance[EVT_1.row][2].Score*Finance[EVT_1.row][2].RelativeWeight) +
(Finance[EVT_1.row][3].Score*Finance[EVT_1.row][3].RelativeWeight) +
(Finance[EVT_1.row][4].Score*Finance[EVT_1.row][4].RelativeWeight) +
(Finance[EVT_1.row][5].Score*Finance[EVT_1.row][5].RelativeWeight) +
(Finance[EVT_1.row][6].Score*Finance[EVT_1.row][6].RelativeWeight));
    discard(EVT_1);
}
The overall score is determined by adding the score of each KPI multiplied by its relative weight.
rule Finance_PScore activated by EVT_1 of
BPEVENT_INFOPAD_ALARM::scorecheckFinance
This rule updates the perspective scores when one of the individual KPI scores has changed.
if (EVT_1.column != "7")
...and it was not the overall perspective score in column FinanceScore or seven.
then
{generate(type : "UpdateBalancedScorecard", value :
"BalancedScorecardscoreupdate", row : EVT_1.row);
    discard(EVT_1);
}
The scorecard is updated.
rule BP ServerUpdateFinance_0_0 activated by EVT_1 of
BP Server::W_COMPLETED
if (EVT_1.PROCESSTEMPLATENAME = "BalancedScorecard") and
(EVT_1.WORKSTEPNAME = "FinanceSC")
then
{Finance[common::common_rules::MonthLookupInfopad[EVT_1.date.month]
[1].monthName+toString(EVT_1.date.year)][1].CurrentValue +=
toFloat(EVT_1.FinancePR);
    discard(EVT_1);
}rule BP ServerUpdateFinance_1_0 activated by EVT_1 of
BP Server::W_COMPLETED
if (EVT_1.PROCESSTEMPLATENAME = "BalancedScorecard") and
(EVT_1.WORKSTEPNAME = "FinanceSC")
then
{Finance[common::common_rules::MonthLookupInfopad[EVT_1.date.month]
[1].nonthName+toString(EVT_1.date.year)][2].CurrentValue ::=
avg(toFloat(EVT_1.FinanceSR),Finance[common::common_rules::
MonthLookupInfopad[EVT_1.date.month][1].nonthName+toString(EVT_1.
date.year)][2].count++,1);
    discard(EVT_1);
}rule BP ServerUpdateFinance_2_0 activated by EVT_1 of
BP Server::W_COMPLETED
if (EVT_1.PROCESSTEMPLATENAME = "BalancedScorecard") and
(EVT_1.WORKSTEPNAME = "FinanceSC")
then
{Finance[common::common_rules::MonthLookupInfopad[EVT_1.date.month]
[1].nonthName+toString(EVT_1.date.year)][3].CurrentValue +=
toFloat(EVT_1.FinancePRG);
    discard(EVT_1);
}rule BP ServerUpdateFinance_3_0 activated by EVT_1 of
BP Server::W_COMPLETED
if (EVT_1.PROCESSTEMPLATENAME = "BalancedScorecard") and
(EVT_1.WORKSTEPNAME = "FinanceSC")
then
{Finance[common::common_rules::MonthLookupInfopad[EVT_1.date.month]
[1].nonthName+toString(EVT_1.date.year)][4].CurrentValue +=
toFloat(EVT_1.FinanceSRG);
    discard(EVT_1);
}rule BP ServerUpdateFinance_4_0 activated by EVT_1 of
BP Server::W_COMPLETED
if (EVT_1.PROCESSTEMPLATENAME = "BalancedScorecard") and (EVT_1.WORKSTEPNAME = "FinanceSC")
then
{Finance[common::common_rules::MonthLookupInfopad[EVT_1.date.month]
[1].nonthName+toString(EVT_1.date.year)][5].CurrentValue ::=
avg(toFloat(EVT_1.FinancePM),Finance[common::common_rules::
MonthLookupInfopad[EVT_1.date.month][1].nonthName+toString(EVT_1.
date.year)][5].count++,1);
    discard(EVT_1);
}rule BP ServerUpdateFinance_5_0 activated by EVT_1 of
BP Server::W_COMPLETED
if (EVT_1.PROCESSTEMPLATENAME = "BalancedScorecard") and
(EVT_1.WORKSTEPNAME = "FinanceSC")
then
{Finance[common::common_rules::MonthLookupInfopad[EVT_1.date.month]
[1].nonthName+toString(EVT_1.date.year)][6].CurrentValue +=
toFloat(EVT_1.FinanceSP);
    discard(EVT_1);
}
Updates the CurrentValue of the first KPI by the value in the dataslot Finance1. This is a dynamic row selection which takes the name of the current month plus the current year to designate the current row. The MonthLookupInfopad holds the names of the months, and the month number looks up the correct name before the year is appended to it.
rule BalancedScorecardimp_BalancedScorecard activated by EVT_1 of
BPEVENT_INFOPAD_ALARM::updateBalancedScorecardScores
This rule updates the overall balanced score when any of the perspective weights in the BalancedScorecardimp infopad are updated. Trigger this rule when the event comes from an alarm condition.
then
{    generate(type : "UpdateBalancedScorecard", value :
    "BalancedScorecardscoreupdate", row :
    BalancedScorecard.rowCount());
    
    discard(EVT_1);
}
This action generates an update BalancedScorecard event which updates the overall score.
rule BalancedScorecard_update activated by EVT_1 of
UpdateBalancedScorecard::BalancedScorecardscoreupdate
Trigger this rule when the event type is UpdateBalancedScorecard and the event value is BalancedScorecardscoreupdate, that is either a perspective score or perspective weight was updated, thus necessitating an update to the overall balanced score.
then
{    BalancedScorecard[EVT_1.row][1].Score :=
    (((BalancedScorecardimp["Finance"]["Current"].Weight*Finance[EVT
    _1.row]["FinanceScore"].Score)+(BalancedScorecardimp["Customer
    "]["Current"].Weight*Customer[EVT_1.row]["CustomerScore"].Score
    )+(BalancedScorecardimp["Internal"]["Current"].Weight*Internal
    [EVT_1.row]["InternalScore"].Score)+(BalancedScorecardimp
    ["Innovation"]["Current"].Weight*Innovation[EVT_1.row]
    ["InnovationScore"].Score))/
    (BalancedScorecardimp["Finance"]["Current"].Weight+
    BalancedScorecardimp["Customer"]["Current"].Weight+
    BalancedScorecardimp["Internal"]["Current"].Weight+
    BalancedScorecardimp["Innovation"]["Current"].Weight));
    
    discard(EVT_1);
}
The overall score is the sum of each perspective score multiplied by the perspective weight. This score is normalized by dividing by the sum of the perspective weights.
rule addRows activated by EVT_1 of
AddBalancedScorecardScorecardRows::monthly
This rule is triggered by a scheduled event with type AddScorecardRows and value monthly. The first time this rule runs, it is triggered by the schedule action in the initialization section. Subsequent triggers are from events scheduled at the end of this rule. The sample implementation updates the rows on a monthly basis.
then
{var addedRowIndex = Finance.add();
This action adds a row to the dynamic infopad FinanceInfopad. The index of the newly created row is stored in addedRowIndex. The following explanations apply to the FinanceInfopad only. The actions for the other perspective infopads are similar.
if(EVT_1.date.month != 12)
    Finance.setRowLabel(addedRowIndex,
    common::common_rules::MonthLookupInfopad[EVT_1.date.month+1][1]
    .nonthName+EVT_1.date.year);
else
    Finance.setRowLabel(addedRowIndex,
    common::common_rules::MonthLookupInfopad[1][1].nonthName+(EVT_1
    .date.year+1));
This action sets the row label for the newly created row. The row label is of the form <monthname><year>, for example, March2005. The infopad MonthLookupInfopad contains the string names of the months. Also, as this rule is run on the last day of the month, you can lookup the name of the current month + 1, with december(12) being a special case. The addedRowIndex value is the row number to add the new label to.
Finance[addedRowIndex][1].count :=
Finance[addedRowIndex-1][1].count;
Finance[addedRowIndex][1].WorstValue :=
Finance[addedRowIndex-1][1].WorstValue;
Finance[addedRowIndex][1].BestValue :=
Finance[addedRowIndex-1][1].BestValue;
Finance[addedRowIndex][1].CurrentValue :=
Finance[addedRowIndex-1][1].CurrentValue;
Finance[addedRowIndex][1].RelativeWeight :=
Finance[addedRowIndex-1][1].RelativeWeight;
Finance[addedRowIndex][1].Score := Finance[addedRowIndex-1][1].Score;
Finance[addedRowIndex][1].Description :=
Finance[addedRowIndex-1][1].Description;
Finance[addedRowIndex][1].Unit := Finance[addedRowIndex-1][1].Unit;
Finance[addedRowIndex][1].NegativeRange :=
Finance[addedRowIndex-1][1].NegativeRange;
The above action initializes the values in the new row by assigning them the current values of the previous one. This is done for all the columns in the new row.
Finance[addedRowIndex]["FinanceScore"].Score :=
Finance[addedRowIndex-1]["FinanceScore"].Score;
Finance[addedRowIndex]["FinanceScore"].RelativeWeight :=
Finance[addedRowIndex-1]["FinanceScore"].RelativeWeight;
The last column must contain only the sum of the relative weights and scores, for this perspective.
addedRowIndex := BalancedScorecard.add();
    if(EVT_1.date.month != 12)
        BalancedScorecard.setRowLabel(addedRowIndex,
    common::common_rules::MonthLookupInfopad[EVT_1.date.month+1][1].
    nonthName+EVT_1.date.year);
    else
    BalancedScorecard.setRowLabel(addedRowIndex,
    common::common_rules::MonthLookupInfopad[1][1].nonthName+(EVT_1
    .date.year+1));
    BalancedScorecard[addedRowIndex][1].Score :=     BalancedScorecard[addedRowIndex-1][1].Score;
The above action performs similar actions for the Main BalancedScorecard infopad as for FinanceInfoPad and the other perspective infopads.
schedule("BalancedScorecardRowCreator", (((YEAR:((NOW).year())/
MONTH:((NOW).month()))+(1*MONTH))-(1*DAY)),type:"
AddBalancedScorecardScorecardRows",value:"monthly");
discard(EVT_1);
The above action schedules the event which creates the rows for the next month.
finalize
{discard(BalancedScorecardimp);
discard(Finance);
discard(Customer);
discard(Internal);
discard(Innovation);
discard(BalancedScorecard);
}
This rule is run when you unload the rules from BPM Events. It cleans up the infopads from memory and the database, and is extremely useful during debugging.