Workflow & Automation

Workflow:


Code-list

Used optionId and customfield_id are described in Objects code-list matrix

Entering Probability value (P)

User got transition screen with the radio buttons containing all possible Probability options.

Field is always optional, but there is a Validator during Assess❗transition to check, if this field got not null value.

In this particular customer, Probability had five options, which are represented by values 1 (less probably) to 5 (extremely probable).

As we need to show word representation to customer, but store number value so we can use it later, we are storing number representation value to dedicated Custom Field "Probability Value".

Probability post-function condition
return (issue.getRawValue("customfield_10104")?.optionId == 10209)

We are using this script with post-function "Set Issue Value (JMWE)". In this example, we are storing number "5" to Custom Field "Probability Value" with condition, if word representation is matching.

Those post function is repeated for each and every value of Custom Field, in this case, five times.

Entering Urgency (U)

User got transition screen with the radio buttons containing all possible Urgency options.

Field is always optional, but there is a Validator during Assess❗transition to check, if this field got not null value.

In this particular customer, Urgency had five options, which are represented by values 1 (trivial) to 5 (cricital).

As we need to show word representation to customer, but store number value so we can use it later, we are storing number representation value to dedicated Custom Field "Urgency Value".


Urgency post-function condition
return (issue.getRawValue("customfield_10103")?.optionId == 10200)

We are using this script with post-function "Set Issue Value (JMWE)". In this example, we are storing number "5" to Custom Field "Urgency Value" with condition, if word representation is matching.

Those post function is repeated for each and every value of Custom Field, in this case, five times.


Calculating Risk Factor field (F) 

Field type is Calculated Number Field (plugin JMCF)

Risk Factor
<!-- @@Formula: (issue.get("customfield_10108") != null ? issue.get("customfield_10108") : 0) * (issue.get("customfield_10110") != null ? issue.get("customfield_10110") : 0) -->

Entering Expected Costs (C)

User got transition screen with the radio buttons containing all possible Expected Costs options ranges.

Field is always optional, but there is a Validator during Assess❗transition to check, if this field got not null value.

In this particular customer, Expected Costs had five options, which are represented by 5 ranges, which can found at Objects code-list matrix.


Assigning users to Authorized to Approve (AtA) on behalf of (F) and (C) matrix.

Field Authorized to Approve is Checkbox type and can be filled with of Company Roles described in Objects code-list matrix.

We are setting Custom Field Authorized to Approve to five groups of roles on behalf of Risk Factor and Expected Costs.

Those groups are represented by Authorized to Approve values objectId and can be found in Objects code-list matrix.

The hardcoded values we are comparing Custom Fields to are defined by Customer and are under NDA. Sorry for not sharing, but you can imagine just some thresholds by putting togethers values of Risk Factor and Expected Costs.

Group MS, K, P and superiors 

optionId 10222,10228,10225,10221,10220,10219,20226,10227
Double.parseDouble(issue.getAsString("customfield_10109")) > 0.5 && 
Double.parseDouble(issue.getAsString("customfield_10109")) < 2.4 && 
issue.getRawValue("customfield_10105")?.optionId == 10210

Group VS and superiors

optionId 10222,10228,10225,10221,10220
(Double.parseDouble(issue.getAsString("customfield_10109")) > 2.5 && 
Double.parseDouble(issue.getAsString("customfield_10109")) < 4.5 && 
issue.getRawValue("customfield_10105")?.optionId == 10210) ||
(Double.parseDouble(issue.getAsString("customfield_10109")) > 0.5 && 
Double.parseDouble(issue.getAsString("customfield_10109")) < 2.5 && 
issue.getRawValue("customfield_10105")?.optionId == 10211)

Group RSZ and superiors

optionId 10222,10228,10225,10221
(Double.parseDouble(issue.getAsString("customfield_10109")) > 4.5 && 
Double.parseDouble(issue.getAsString("customfield_10109")) < 13 && 
issue.getRawValue("customfield_10105")?.optionId == 10210) ||
(Double.parseDouble(issue.getAsString("customfield_10109")) > 2.5 && 
Double.parseDouble(issue.getAsString("customfield_10109")) < 4.5 && 
issue.getRawValue("customfield_10105")?.optionId == 10211) ||
(Double.parseDouble(issue.getAsString("customfield_10109")) > 0.5 && 
Double.parseDouble(issue.getAsString("customfield_10109")) < 2.5 && 
issue.getRawValue("customfield_10105")?.optionId == 10212)

Group TR, RZP and superiors

optionId 10222,10228,10225
(Double.parseDouble(issue.getAsString("customfield_10109")) > 4.5 &&
Double.parseDouble(issue.getAsString("customfield_10109")) < 13 && 
issue.getRawValue("customfield_10105")?.optionId == 10211) ||
(Double.parseDouble(issue.getAsString("customfield_10109")) > 2.5 && 
Double.parseDouble(issue.getAsString("customfield_10109")) < 4.5 && 
issue.getRawValue("customfield_10105")?.optionId == 10212) ||
(Double.parseDouble(issue.getAsString("customfield_10109")) > 0.5 && 
Double.parseDouble(issue.getAsString("customfield_10109")) < 2.5 && 
issue.getRawValue("customfield_10105")?.optionId == 10213)

Group RD (have no superiors)

optionId 10222
(Double.parseDouble(issue.getAsString("customfield_10109")) > 14 || 
issue.getRawValue("customfield_10105")?.optionId == 10214) ||
(Double.parseDouble(issue.getAsString("customfield_10109")) > 4.5 && 
Double.parseDouble(issue.getAsString("customfield_10109")) < 23 && 
(issue.getRawValue("customfield_10105")?.optionId == 10212 || 
issue.getRawValue("customfield_10105")?.optionId == 10213 )) ||
(Double.parseDouble(issue.getAsString("customfield_10109")) > 2.5 && 
Double.parseDouble(issue.getAsString("customfield_10109")) < 4.5 && 
issue.getRawValue("customfield_10105")?.optionId == 10213)

Reflecting group checkboxes as rights (AtA) in transition validator

Predisposition: Global Project Roles are 1:1 set to have very same names as the values of Authorize to Approve (AtA) Custom field.

Because there is no method to return list of roles for currentUser, we are using this logic:

Script validator logic
1) We are comparing value in (AtA) with every possible value which is defined by name of Project Role and optionId of Custom Fields Authorized to Approve.
AND
2) We are comparing, if currentUser is in same Project Role. (hardcoded)
IF
Both condition are TRUE, transition is allowed.
ELSE
Transition is blocked and user got an error message, that he is not authorized to this Approval.


Approve Transition Validator
(issue.get("customfield_10111")?.any{it.value == "K: Level 1, Role 2"} &&
currentUser.isInProjectRole("K: Level 1, Role 2",issue.get("Project"))) || 
(issue.get("customfield_10111")?.any{it.value == "MS: Level 1, Role 1"} &&
currentUser.isInProjectRole("MS: Level 1, Role 1",issue.get("Project"))) || 
(issue.get("customfield_10111")?.any{it.value == "P: Level 1, Role 3"} &&
currentUser.isInProjectRole("P: Level 1, Role 3",issue.get("Project"))) || 
(issue.get("customfield_10111")?.any{it.value == "Level 4, Role 1"} &&
currentUser.isInProjectRole("TR: Level 4, Role 1",issue.get("Project"))) || 
(issue.get("customfield_10111")?.any{it.value == "VE: NOT IN USE"} &&
currentUser.isInProjectRole("VE: NOT IN USE",issue.get("Project"))) || 
(issue.get("customfield_10111")?.any{it.value == "VK: NOT IN USE"} &&
currentUser.isInProjectRole("VK: NOT IN USE",issue.get("Project"))) || 
(issue.get("customfield_10111")?.any{it.value == "VS: Level 2, Role 1"} &&
currentUser.isInProjectRole("VS: Level 2, Role 1",issue.get("Project"))) || 
(issue.get("customfield_10111")?.any{it.value == "RD: Level 5, Role 1"} &&
currentUser.isInProjectRole("RD: Level 5, Role 1",issue.get("Project"))) ||
(issue.get("customfield_10111")?.any{it.value == "RZP: Level 4, Role 2"} &&
currentUser.isInProjectRole("RZP: Level 4, Role 2",issue.get("Project"))) ||
(issue.get("customfield_10111")?.any{it.value == "RZS: Level 4, Role 2"} &&
currentUser.isInProjectRole("RZS: Level 4, Role 2",issue.get("Project")))