WITH TREE (TREE_NODE, TREE_NODE2, TREE_NODE3, TREE_LEVEL_NUM, PARENT_NODE_NAME, TREE_NODE_NUM, TREE_NODE_NUM_END, STATUS, MANAGER_POSN, MANAGER_NAME, DESCR ) AS (
SELECT TREE_NODE
,TREE_NODE
,TREE_NODE
, TREE_LEVEL_NUM
, PARENT_NODE_NAME
,TREE_NODE_NUM
,TREE_NODE_NUM_END
, 'A'
, ' '
, ' '
, 'My Corp. Descr'
FROM pstreenode b
WHERE b.tree_name = 'DEPT_SECURITY'
AND B.SETID = 'SHARE'
AND b.tree_level_num = 1
AND B.EFFDT=(
SELECT MAX(EFFDT)
FROM PSTREENODE PSTREENODE
WHERE PSTREENODE.SETID=B.SETID
AND PSTREENODE.SETCNTRLVALUE=B.SETCNTRLVALUE
AND PSTREENODE.TREE_NAME=B.TREE_NAME
AND PSTREENODE.EFFDT<=TO_DATE(TO_CHAR(SYSDATE,'YYYY-MM-DD'),'YYYY-MM-DD'))
UNION ALL
SELECT B.TREE_NODE
,substr('>>>>>>>>>'
, 1
, (B.TREE_LEVEL_NUM-1))||B.TREE_NODE
,LPAD(' '
, (B.TREE_LEVEL_NUM-1)) ||B.TREE_NODE
, B.TREE_LEVEL_NUM
, B.PARENT_NODE_NAME
,B.TREE_NODE_NUM
, B.TREE_NODE_NUM_END
, C.EFF_STATUS
, C.MANAGER_POSN
, CASE C.MANAGER_POSN WHEN ' ' THEN '' ELSE (
SELECT X.NAME
FROM PS_ZZ_IDM_DATA X
WHERE X.POSITION_NBR = C.MANAGER_POSN
AND X.HR_STATUS IN ('A', 'L')
AND ROWNUM = 1) END CASE,C.DESCR
FROM TREE, pstreenode b, PS_DEPT_TBL C
WHERE b.tree_name = 'DEPT_SECURITY'
AND B.SETID = 'SHARE'
AND TREE.TREE_NODE_NUM = B.PARENT_NODE_NUM
AND B.TREE_NODE = C.DEPTID
AND B.SETID = C.SETID
AND C.EFFDT=(
SELECT MAX(EFFDT)
FROM PS_DEPT_TBL DEPT_TBL
WHERE DEPT_TBL.SETID=C.SETID
AND DEPT_TBL.DEPTID=C.DEPTID
AND DEPT_TBL.EFFDT<=TO_DATE(TO_CHAR(SYSDATE,'YYYY-MM-DD'),'YYYY-MM-DD'))
AND B.EFFDT=(
SELECT MAX(EFFDT)
FROM PSTREENODE PSTREENODE
WHERE PSTREENODE.SETID=B.SETID
AND PSTREENODE.SETCNTRLVALUE=B.SETCNTRLVALUE
AND PSTREENODE.TREE_NAME=B.TREE_NAME
AND PSTREENODE.EFFDT<=TO_DATE(TO_CHAR(SYSDATE,'YYYY-MM-DD'),'YYYY-MM-DD')) )
SELECT TREE_NODE
, TREE_NODE2||'-'||DESCR
, TREE_NODE3||'-'||DESCR
,CASE TREE_LEVEL_NUM WHEN 1 THEN 'Company' WHEN 2 THEN 'Executive' WHEN 3 THEN 'Division' WHEN 4 THEN 'Region' WHEN 5 THEN 'Business' WHEN 6 THEN 'Function' WHEN 7 THEN 'Team' WHEN 8 THEN 'Department' ELSE 'LEVEL9' END CASE
,TREE_LEVEL_NUM
,MANAGER_NAME
,STATUS
,PARENT_NODE_NAME
,TREE_NODE_NUM
,TREE_NODE_NUM_END
,DESCR
FROM TREE
WHERE status = 'A'
ORDER BY TREE_NODE_NUM
SELECT TREE_NODE
,TREE_NODE
,TREE_NODE
, TREE_LEVEL_NUM
, PARENT_NODE_NAME
,TREE_NODE_NUM
,TREE_NODE_NUM_END
, 'A'
, ' '
, ' '
, 'My Corp. Descr'
FROM pstreenode b
WHERE b.tree_name = 'DEPT_SECURITY'
AND B.SETID = 'SHARE'
AND b.tree_level_num = 1
AND B.EFFDT=(
SELECT MAX(EFFDT)
FROM PSTREENODE PSTREENODE
WHERE PSTREENODE.SETID=B.SETID
AND PSTREENODE.SETCNTRLVALUE=B.SETCNTRLVALUE
AND PSTREENODE.TREE_NAME=B.TREE_NAME
AND PSTREENODE.EFFDT<=TO_DATE(TO_CHAR(SYSDATE,'YYYY-MM-DD'),'YYYY-MM-DD'))
UNION ALL
SELECT B.TREE_NODE
,substr('>>>>>>>>>'
, 1
, (B.TREE_LEVEL_NUM-1))||B.TREE_NODE
,LPAD(' '
, (B.TREE_LEVEL_NUM-1)) ||B.TREE_NODE
, B.TREE_LEVEL_NUM
, B.PARENT_NODE_NAME
,B.TREE_NODE_NUM
, B.TREE_NODE_NUM_END
, C.EFF_STATUS
, C.MANAGER_POSN
, CASE C.MANAGER_POSN WHEN ' ' THEN '' ELSE (
SELECT X.NAME
FROM PS_ZZ_IDM_DATA X
WHERE X.POSITION_NBR = C.MANAGER_POSN
AND X.HR_STATUS IN ('A', 'L')
AND ROWNUM = 1) END CASE,C.DESCR
FROM TREE, pstreenode b, PS_DEPT_TBL C
WHERE b.tree_name = 'DEPT_SECURITY'
AND B.SETID = 'SHARE'
AND TREE.TREE_NODE_NUM = B.PARENT_NODE_NUM
AND B.TREE_NODE = C.DEPTID
AND B.SETID = C.SETID
AND C.EFFDT=(
SELECT MAX(EFFDT)
FROM PS_DEPT_TBL DEPT_TBL
WHERE DEPT_TBL.SETID=C.SETID
AND DEPT_TBL.DEPTID=C.DEPTID
AND DEPT_TBL.EFFDT<=TO_DATE(TO_CHAR(SYSDATE,'YYYY-MM-DD'),'YYYY-MM-DD'))
AND B.EFFDT=(
SELECT MAX(EFFDT)
FROM PSTREENODE PSTREENODE
WHERE PSTREENODE.SETID=B.SETID
AND PSTREENODE.SETCNTRLVALUE=B.SETCNTRLVALUE
AND PSTREENODE.TREE_NAME=B.TREE_NAME
AND PSTREENODE.EFFDT<=TO_DATE(TO_CHAR(SYSDATE,'YYYY-MM-DD'),'YYYY-MM-DD')) )
SELECT TREE_NODE
, TREE_NODE2||'-'||DESCR
, TREE_NODE3||'-'||DESCR
,CASE TREE_LEVEL_NUM WHEN 1 THEN 'Company' WHEN 2 THEN 'Executive' WHEN 3 THEN 'Division' WHEN 4 THEN 'Region' WHEN 5 THEN 'Business' WHEN 6 THEN 'Function' WHEN 7 THEN 'Team' WHEN 8 THEN 'Department' ELSE 'LEVEL9' END CASE
,TREE_LEVEL_NUM
,MANAGER_NAME
,STATUS
,PARENT_NODE_NAME
,TREE_NODE_NUM
,TREE_NODE_NUM_END
,DESCR
FROM TREE
WHERE status = 'A'
ORDER BY TREE_NODE_NUM
The end result of my query returned the Department ID as the tree node, a column representing the hierarchy with node and description indented with greater than signs and one column indented with spaces, the description of the level from the tree, the tree level number, the manager of the department ( this is from a custom table, you can get the manager's name from the PERSONAL_DATA if you don't use position management), effective status, parent node name, tree node number and the tree node number end and finally the Description from the department table.
Below is an example of the end product in SQL.
COMPANY-Department Descr Manager Name
>1018-Department Descr Manager Name
>>1330-Department Descr Manager Name
>>>1185-Department Descr Manager Name
>>>1186-Department Descr Manager Name
>>>>>>>01000304-Department Descr Manager Name
>>>1385-Department Descr Manager Name
>>>1145-Department Descr Manager Name
>>>>>>>5020916-Department Descr Manager Name
>>>1031-Department Descr Manager Name
>>>>1205-Department Descr Manager Name
>>>>>>>1170980-Department Descr Manager Name
>>>>1331-Department Descr Manager Name
>>>>>>>1110102-Department Descr Manager Name
>>>>>>>1110980-Department Descr Manager Name
While this is pretty decent for SQL it was still difficult to keep inline what departments reported to other departments. So I thought about putting this information into a PeopleSoft page that would allow the user to view this data, however be able to collapse nodes and expand nodes to visualize the hierarchy. I was surprised to find out how easy this was with the use of a jQuery Tree Project found on GITHUB. I created a page with an HTML area and in the page Activate PeopleCode I loaded a view that I created of off the CTE SQL above.
The PeopleCode was probably the most difficult part of this process. We use an unstructured tree for our organization structure. This means that a level three can have a level three as a parent or a level eight department can report to a level three division. So when building the unordered list HTML for the tree I needed to keep track of how many levels to break the list so that the roll-up would work properly. Below is my page PeopleCode to build my unordered list for the HTML area on my page.
Local Rowset &ORG_STRUCTURE;
Local string &new_line = "<li><span><span class=""KNOCKOUT"">";
Local string &sibling = "</span></span></li>" | Char(12) | Char(13);
Local string &child = "</span></span><ul>" | Char(12) | Char(13);
Local string &End_Family = "</ul>" | Char(12) | Char(13) | "</li>" | Char(12) | Char(13);
<* array of parents is used to figure out then number of end family's I need to put in the HTML area. If the tree writes down to
rem level 8, a department, and the next row is a level three, a division, then I need to evaluate the parent of this new row against the array
rem to see how many closing ul and li tags are needed to properly close this group.*>
Local array of string &PARENTS = CreateArrayRept("", 0);
rem a view build on my CTE SQL;
&ORG_STRUCTURE = CreateRowset(Record.ZZ_DEPT_SEC_VW);
&ROWS = &ORG_STRUCTURE.Fill("where EFF_STATUS = 'A'");
REM &tree will be our HTML tree data;
Local string &tree = "";
For &X = 1 To &ROWS
If &X = 1 Then
&tree = &new_line | &ORG_STRUCTURE(&X).ZZ_DEPT_SEC_VW.TREE_NODE.Value;
Else
&value = &ORG_STRUCTURE(&X).ZZ_DEPT_SEC_VW.TREE_LEVEL_NUM.Value - &ORG_STRUCTURE(&X - 1).ZZ_DEPT_SEC_VW.TREE_LEVEL_NUM.Value;
Evaluate &value
When = 0
REM NO CHANGE IN TREE LEVEL BUT NEED TO EVALUATE TWO CONDITIONS;
REM DOES THE NEW ROW REPORT TO THE SAME LEVEL EXAMPLE REGION REPORTS TO A REGION;
REM TWO DOES THIS LEVEL REPORT TO HIGHER LEVEL THAN THE PREVIOUS ROW.
REM EXAMPLE Division reports to Region next division reports to executive;
If &ORG_STRUCTURE(&X).ZZ_DEPT_SEC_VW.PARENT_NODE_NAME.Value = &ORG_STRUCTURE(&X - 1).ZZ_DEPT_SEC_VW.TREE_NODE.Value Then
&tree = &tree | &child;
&PARENTS.Push(&ORG_STRUCTURE(&X).ZZ_DEPT_SEC_VW.PARENT_NODE_NAME.Value);
Break;
Else
If &ORG_STRUCTURE(&X).ZZ_DEPT_SEC_VW.PARENT_NODE_NAME.Value <> &ORG_STRUCTURE(&X - 1).ZZ_DEPT_SEC_VW.PARENT_NODE_NAME.Value Then
&tree = &tree | &sibling;
&LOOP = True;
While &LOOP
&POP = &PARENTS.Pop();
If &POP <> &ORG_STRUCTURE(&X).ZZ_DEPT_SEC_VW.PARENT_NODE_NAME.Value Then
&tree = &tree | &End_Family;
Else
&PARENTS.Push(&ORG_STRUCTURE(&X).ZZ_DEPT_SEC_VW.PARENT_NODE_NAME.Value);
&LOOP = False;
End-If;
End-While;
Else
&tree = &tree | &sibling;
End-If;
End-If;
Break;
When > 0
&tree = &tree | &child;
&PARENTS.Push(&ORG_STRUCTURE(&X).ZZ_DEPT_SEC_VW.PARENT_NODE_NAME.Value);
Break;
When-Other
&tree = &tree | &sibling;
&LOOP = True;
While &LOOP
&POP = &PARENTS.Pop();
If &POP <> &ORG_STRUCTURE(&X).ZZ_DEPT_SEC_VW.PARENT_NODE_NAME.Value Then
&tree = &tree | &End_Family;
Else
&PARENTS.Push(&ORG_STRUCTURE(&X).ZZ_DEPT_SEC_VW.PARENT_NODE_NAME.Value);
&LOOP = False;
End-If;
End-While;
End-Evaluate;
&tree = &tree | &new_line | &ORG_STRUCTURE(&X).ZZ_DEPT_SEC_VW.TREE_NODE.Value | "-" | &ORG_STRUCTURE(&X).ZZ_DEPT_SEC_VW.DESCR.Value | " <span class=""KNOCKOUT""> [" | &ORG_STRUCTURE(&X).ZZ_DEPT_SEC_VW.MANAGER_NAME.Value | "]</span>";
End-If;
End-For;
REM SERVE UP YOU OWN LOCAL VERSION OF THE LIBRARY IS BEST, TO SEE EXAMPLS OF HOW TO DO THIS CHECK OUT Jim Marion's Blog or his book;
<*
&Jq_CookieJS = GenerateScriptContentURL(%Portal, %Node, Record.WEBLIB_APT_JSL, Field.ISCRIPT1, "FieldFormula", "IScript_Jq_CookieJS");
&Jq_TreeJS = GenerateScriptContentURL(%Portal, %Node, Record.WEBLIB_APT_JSL, Field.ISCRIPT1, "FieldFormula", "IScript_Jq_TreeJS");
&Jq_LibJS = GenerateScriptContentURL(%Portal, %Node, Record.WEBLIB_APT_JSL, Field.ISCRIPT1, "FieldFormula", "IScript_jQuery_1_7_1");
*>
ZZ_ORG_WRK.HTMLAREA.Value = GetHTMLText(HTML.ZZ_ORG_STRUCTURE1, &tree);
Below is the HTML object I build for my demo:
<script type="text/javascript" src="https://github.com/pioz/jquery-tree/raw/master/jquery.min.js"></script>
<script type="text/javascript" src="https://github.com/pioz/jquery-tree/raw/master/jquery.cookie.js"></script>
<script type="text/javascript" src="https://github.com/pioz/jquery-tree/raw/master/jquery.tree.js"></script>
<script type="text/javascript">
$(document).ready(function( ) {
$('ul.org').tree({default_expanded_paths_string : '0/0,0/1,0/2'});
});
</script>
<table border="0">
<tr>
<td></td>
</tr>
<tr>
<td><div >
<span class="PAPAGETITLE"> Organizational Structure</span>
</div>
</td>
</tr>
<tr>
<td>
<a class="PSHYPERLINK" onclick=jQuery('ul.org').expand()>Expand All</a>
<a class="PSHYPERLINK" onclick=jQuery('ul.org').collapse()>Collapse All</a>
</td>
</tr>
<tr>
<td>
<span class="KNOCKOUT">
<ul class="org" >
%Bind(:1)
</ul>
</span>
</td>
</tr>
</table>
The use of JavaScript Libraries from third party websites is not recommended, however they will work for the purpose of this demo. However, if your looking for cools ways to server up JavaScript and JavaScript libraries to the browser, check out Jim Marion's blog.
Below is example of how the jQuery tree works with a mocked up tree data. You can expand the node of the tree by clicking on the Arrow to the left of the Node number. Other ideas might be to make this tree searchable?
<script type="text/javascript" src="https://github.com/pioz/jquery-tree/raw/master/jquery.cookie.js"></script>
<script type="text/javascript" src="https://github.com/pioz/jquery-tree/raw/master/jquery.tree.js"></script>
<script type="text/javascript">
$(document).ready(function( ) {
$('ul.org').tree({default_expanded_paths_string : '0/0,0/1,0/2'});
});
</script>
<table border="0">
<tr>
<td></td>
</tr>
<tr>
<td><div >
<span class="PAPAGETITLE"> Organizational Structure</span>
</div>
</td>
</tr>
<tr>
<td>
<a class="PSHYPERLINK" onclick=jQuery('ul.org').expand()>Expand All</a>
<a class="PSHYPERLINK" onclick=jQuery('ul.org').collapse()>Collapse All</a>
</td>
</tr>
<tr>
<td>
<span class="KNOCKOUT">
<ul class="org" >
%Bind(:1)
</ul>
</span>
</td>
</tr>
</table>
The use of JavaScript Libraries from third party websites is not recommended, however they will work for the purpose of this demo. However, if your looking for cools ways to server up JavaScript and JavaScript libraries to the browser, check out Jim Marion's blog.
Below is example of how the jQuery tree works with a mocked up tree data. You can expand the node of the tree by clicking on the Arrow to the left of the Node number. Other ideas might be to make this tree searchable?
The use of the arrow to expand and collapse the nodes allows you to turn the tree node description into a hyperlink to the department table for end users to to make updates. Other uses for this type in tree data could be building a reports to tree for position management or even a quick view of supervisor hierarchy. Can you think of any ideas or uses in your organization?