When we last left our heroes, they were blazing a trail of understanding through the vastness space that we call organizational management (uggg….really need to work on that opener). Anyways….if you read part 1 of this (Understanding and Coding for OM Inheritance: Part 1 – Basics ), then you should now have at least a “dangerous” knowledge of OM inheritance and how it all works.
Now, let us talk about how we would go about writing our own code to detect and read inherited values for such things as reporting, custom classes or HCM Processes and Forms generic services (a topic near and dear to my heart haha). I had actually looked around the “interwebz” (via Google searches of course) and did not find much information or help from others on doing this although I found many posts from people asking “how” to do it (hence the inspiration for sharing this knowledge now in this way).
The first thing to understand is table T77S0 (yes, that is a zero on the end….there is another similarly named table with an “O” on the end but that is not what we want! Great job with the naming there, SAP! haha) Table T77S0 is where all the “magic” happens. More to the point, it is our configuration table that controls OM inheritance.
At some point in our code, we want to first read this table. We might see values such as:
The important fields for us are as follows:
- INHIC : If this value is set to “X”, this means that positions with inherit “Company Code” from their related Org Unit (ie.Obligatory/Mandatory inheritance of company code is active. Also keep in mind, this means the assignment is forced and not an option to change.). If this value is not “X”, it means that we can assign positions to different company codes than their related Org Unit. This is typically not set to “X” unless we want all positions to have the same company code (smaller organization maybe?).
- INHIH : Same idea as INHIC but for “Controlling Area”. If set to “X”, it is assigned/inherited from the related Org Unit. If not, then a position can be assigned a different controlling area as the one assigned to its related Org Unit.
- INHS : This is typically our most important field to read as it is more flexible in its inheritance (it says that inheritance is “possible” but not mandatory as the other switches do). If this field is assigned the value of “X”, then our position can inherit any one or more of many Account Assignment features (Controlling Area, Company Code, Business Area and/or Personnel Area/Subarea).
(* keep in mind that although the documentation refers to “position” for inherited values, the same applies to “child” Org Units as well.)
You might notice I did not mention a setting for “Cost Center” in there. Because cost center inheritance has a bit different implications as it ties into the FICO (Financial Controlling) module as well, we will discuss how to handle that one later in this document.
Infotype 1008….or actually, “the case of the missing infotype 1008”
When trying to locate say the Personnel Area assigned to our position, the first thing we will often do is either used a standard function or select on infotype 1008 (table HRP1008) to find our entry.
However, if this value is inherited, we will not have an infotype 1008 entry for our object (position).
If we look at transaction PPOME, we might see something like this for our object (ex. a position).
This tells us that it is inheriting Cost Center, Company Code, Personnel Area and Personnel Subarea from a higher level org unit. The logical thing to do then is to look “behind” PPOME and figure how it presents this to us like that. I will save you some time…it is all in..
function group: RHOMDETAIL_APPL
We pretty much will follow what we see in the module read_and_inherit.
Shortly after our “config check”, you will notice this:
This simply says that if we are looking at a position, let’s just make sure we actually allow for inheritance by checking our configuration (table T77S0) mentioned before. But then a bit later, you will see…
CALL FUNCTION ‘RH_CHECK_ACC_INPUT’
This is where all the magic happens! This function will return any inherited controlling area (KOKRS), business area (GSBER), company code (BUKRS), personnel area (PERSA) and personnel subarea (BTRTL) as well as the information on what object type and the object ID for “who” they are inheriting the values from:
If is is not immediately apparent, every returned parameter with “INH_” in front of it is the “inherited” value. So for example, if we look at personnel area:
- INH_PERS_AREA: this is the inherited personnel area value
- INH_PERS_SUB_AREA: this is the inherited personnel subarea value (it comes along with the pers. area)
- INH_OTYPE_PERS_AREA: object type of the object from which we inherit the personnel area (for example, if we inherit from an org unit, this will be “O”)
- INH_OBJID_PERS_AREA: object ID of the object from which we inherit the personnel area (for example, this would be the org unit ID number)
- INH_BEGDA_PERS_AREA: “begin date” for the object that our object is inheriting from
Inherited Cost Center
As with infotype 1008, if we want to find our related cost center to our position, we usually utilize standard SAP function or select on infotype 1001 (relations) to find our “related” cost center (relation A011). But again, what do we do when this brings back nothing? That is a good sign it is being inherited from elsewhere. Again, as with Company Code/Controlling Area/Business Area/Pers. Area/Subarea, we can save ourselves a lot of time and headaches by not trying to “reinvent the wheel” and again, just follow the example/lead of transaction PPOME.
Within the module call to handle_main_cc , you see it first loads up an “input” structure that is just our object type and id (ex. “S” for position type and the position ID number) as follows:
And then, it simply calls standard SAP function RH_COSTCENTER_OF_OBJECT_GET.
(*note: function RH_COSTCENTER_OF_OBJECT_GET is actually really cool and powerful. We can pass over a table of input “objects” and it will find all the associated cost centers…and more…for us. Very useful on to remember!)
The returned structure maincc_tab will contain the cost center assigned to our object (whether direct relation or inherited). But how do we know which is which? Well, because this VERY cool return structure has a “flag” field called inherited that will be “X” if we inherit the cost center….and it also passed us the object type and object ID for where we inherit from!!!! VERY COOL!!!!
- SCLAS = K (cost center)
- SOBID = our cost center value in the format “(cost center)(controlling area)” or KOSTL(10)+KOKRS(4) so for example, we might have 0000124340US01
- INHERITED = flag (if “X” then it is inherited)
- INH_OTYPE = object type inherited from
- INH_OBJID = object ID of the object it inherits the cost center from
- POSITION_OTYPE = S (position)
- POSITION_OBJID = position object ID (redundant if the original object is a position)
Putting It All Together
So now we know where to find/read all these inherited values, how do we best put this new found knowledge to good use? …and by “good”, I mean easily reusable for ourselves and others (haha). Well, for me, I have a handy little class that deals strictly with finding inherited values for me. It is simply a matter of making “wrapper” methods around the previously mentioned standard functions and then we can use them as needed. For example,
The inputs to my public methods area actually all quite simple. I allow an object type, and object idea and an optional “begin date” and “end date” (which if are not passed are set to today’s date and 12/31/9999 respectively). In this way, I can use this to find inheritance on any object (be it a position/object type “S” or org unit/object type “O”).
The private class GET_INHERITED_VALUES is a wrapper around the function RH_CHECK_ACC_INPUT along with a bit of other business logic in there (like checking the config table T77S0 flags as needed). Then I have other public classes that allow me to call that private class and only get the values I want (for example GET_INHERITED_PERSA only returns me the inherited personnel area and subarea). In that one, I keep all the “fetch” code in my one private method so I only have to adjust there if needed as opposed to having the same logic in each of my public classes all acting as wrappers themselves around the standard function. Lastly, the public method GET_INHERITED_COSTCENTER is itself just a wrapper around standard function RH_COSTCENTER_OF_OBJECT_GET,but I have additional code in there that makes sure we only return an inherited cost center (do not care about others as we can handle that elsewhere…remember, our method has a specific purpose here…inherited cost center only! haha).
With that, you now have your own easy way to read inheritance values while also considering configuration.