HCM Processes & Forms: Why FIELD INFO is so important in your Generic Services!
If you ever get involved with HCM P&F in any way, you most assuredly will at some point have to create your own generic service. I will not go into detail of what a “generic service” is, but let us just think of them as a way for us to do in HCM P&F what the configuration will not directly do…..like a way to mingle in little bits of executed custom code in with our configuration….which come to think of it, sounds absolutely scary! (haha)
Anyways, time and again, I have either “inherited” code from previous developers, have to review code for people who are learning HCM P&F, or see code examples/tutorials on the web, and one of my biggest “pet peeves” is when they have not defined anything in the GET_FIELD_INFO method (or they do so half-heartedly by not defining all of their fields). When I ask for a reason, the response is often, “Well, if I don’t define them, they still get passed over.”. Well, yes….that is true…but then you are relying on the framework to guess how you want to use them, and not to mention, you open yourself up for all manner of “bugs”. You can often see this happen as follows….let’s say you define a form field in config as “MY_FIELD” with data element “CHAR25″….you then map this form field to your generic service operation which does have the field assigned but you have not defined it in GET_FIELD_INFO…..once you do this, you will notice that it will now attempt to “match up” your form field’s data element to the operation’s field data element….which guess what?…..it is not defined! ….so it will CLEAR out your form field’s data type assignment. But what does that hurt? Well….let’s see….first off, you will get a warning on checking your form scenario (Utilities-> Check Inconsistencies)…..second, if you go to your “rules” configuration, your form field is no longer listed as an option…third (and worse!), when you got to your form layout that your field may be used on or you want to add it too, the field is no longer available and if it was there, you get a nasty error message. So yes…put in the extra time up front to save you LOTS of headaches in the end! But how do we define the “field info” and what all can we do with it? Come on….follow me!……
If you are familiar with Generic Services for HCM P&F, then these methods should of course look familiar to you (they better!!!! haha), and we are especially interested in the method GET_FIELD_INFO.
This method is VERY important because it defines what our field is called and how it is used (data type/element). Think of this method like creating a “library” of all of our “public” fields/parameters/attributes to allow us to “interface” with our service (and “operations”…which we can think of as our public “methods” in a sense). When we define our operations, we assign what fields we use with the operation. You might see it in code as something like…
Here, you can see that we just keep adding fields to the “fieldnames” table and then pass the whole thing when we assign it to the “operation” structure which then gets appended (“added to the pile”) to all of our “operations” which is what the method returns. Using our class method analogy, you can now think of this as we just defined a method called “CHECK_TABLE” for our “class”(our generic service) ,and this method takes the signature/parameters COL_INDEX, COL_NAME, COL_DATE, COL_FREEFORM and COL_NO_DELETE. It would be like setting ourselves up to do a method call in code:
CALL METHOD my_class->CHECK_TABLE ( CHANGING col_index = l_index
col_name = l_name
col_date = l_date
col_freeform = abap_true
col_no_delete = abap_false ).
So over in the HRASR_DT transaction (Design Time) when we are configuring our generic services, we select which operation(s) from our service we want to use. Guess how the Design Time knows which operations to populate the drop-down with? It actually calls the GET_OPERATIONS method of our service! And it does not take a genius (or even me….haha) to figure out when we map our form scenario fields to our operation, how it knows what fields are possible for that operation (*hint…remember in the GET_OPERATIONS method where we defined what fieldnames are used in the operation? Yep!). And to wrap this all up, it then knows the details about our fields (like data element) because it then calls GET_FIELD_INFO to know how our service uses/defines the field!!!! So now, you should understand why it is very important that you take the time and care to fully define all of your fields in the GET_FIELD_INFO method.
In the GET_FIELD_INFO method, our implementations typically look like….
You simply start looping through and building up your field definitions. It is easiest just to “loop” and use a case statement to respond to each fieldname. If you want to be real “creative”, you can add your own error handling to have a “when others” in your case statement to catch anything not defined, and throw back a error/warning to the user to let them know that a field they are trying to map over has not been defined. That is just a matter of your own preference as it will not affect anything at runtime.
Now, of course, if you want to be a very “detailed” developer, you should define your field names and data element names as constants (public/private constants as static class attributes) and then reference those in the code in case you ever need to change the names of the field/element used. However, for “readability”, I left the above code “as is” (yeh….that’s it…that excuse sounds good! haha) Keep in mind that the names we used in our GET_OPERATIONS method as the “fieldnames” should match the names we use in the GET_FIELD_INFO method as our field names (as well as anywhere else in our code like DO_OPERATIONS when looking through the datasets), so using constants helps insure these do match up! (* I may or may not have had to hunt down strange bugs before where I “fat fingered” typing the names directly, and they did not match. haha)
The “field info” that we define for each of our fields is based on structure HRASR00GS_FIELD_INFO.
At the very least, you should always defined the FIELDNAME and FIELD_DATA_ELEMENT_NAME. The others have very specific usage which we will further discuss. Not setting/assigning/defining them directly in your code will assume the “default” setting for them (false or empty/blank). You do not necessarily have to define them if you can assume anyone else will understand this too….or you really just want to be a pain for whoever follows you. (haha) You can look at SAP’s many “standard” generic services (and many more now with HR Renewal 2.0!), however, be warned….many are incomplete also and some are not consistent in how/why they use some of the fields (like “reference field”). It will give you some ideas, but I think you can figure out how best to define your fields. Let’s look at each possible setting in detail….
I am going to blow your mind here…..but hold on…..this is the name of your field!!!! WOW!!!…right?!?! haha Yes…this is just “what we want to call our field”…like defining a variable name. However, it is often best to make sure that you name your field on the generic service side exactly what you call it over on your form scenario config’s “form fields” side of things. They do not have to match, but it sure makes life a whole lot easier.
Again….hold onto your seat…..this is simply the name of the data element that your field should be defined “like”. So for example, if you have defined field “address_street” and want it to be like data element PAD_STRAS, then you will set this is: field_data_element_name = ‘PAD_STRAS’. It is important that your data elements match up. Whatever you defined on the form scenario’s form fields configuration for your field’s data element should match what you assign your corresponding generic service field.
SUPPORTS_VALUE_HELP and SUPPORTS_DEFAULT_VALUE
Think of this more as “documentation purposes” now. There used to be a bit stricter checking on these fields as you can see in these comments/notes/patches…which themselves have since been commented out (as of HR Renewal 2).
Basically, if you had marked this field as “supports default value” yet in your configuration it was not set as getting a default value from this service, you would get a warning. The same kind of thing would happen if you had marked the field to “support value help” but had not set it for “input help” in configuration.
DEPENDENT_FIELDS_FOR_DEFAULT and DEPENDENT_FIELDS_FOR_VALUEHELP
These are some “interesting” ones we can set. These are actually “tables of fields” that we assign (like when we assign “fieldnames” to our operations in the GET_OPERATIONS method). Again, this is more of a “sanity” check to warn us if our configuration does not match what the operation expects.
For example, suppose we want to populate our own “value help” for Personnel Subarea but of course, the values we show/display are going to depend on the Personnel Area. We might define something like….
field_info-fieldname = ’emp_persa’.
field_info-field_data_element_name = ‘PERSA’.
APPEND field_info TO field_infos.
field_info-fieldname = ’emp_btrtl’.
field_info-field_data_element_name = ‘BTRTL’.
dependent_field = ’emp_persa’.
APPEND dependent_field TO field_info-dependent_fields_for_valuehelp.
APPEND field_info TO field_infos.
….so then, the framework will “look” and say “ok…we have the field for Personnel Subarea mapped to this operation….but is the Personnel Area field (‘EMP_PERSA’) also mapped to this?”…..if we mapped the Personnel Subarea but did not map/pass the Personnel Area, it will actually not “warn” us….it simply “unflags” the “supports value help” field. (you can see this in method GET_GEN_SRV_OPERATIONS_INFO of class CL_HRASR00_DT_FSCN_GS_DATA). Otherwise, these fields are only checked in one other place (class method) which does nothing really other than take whatever value is passed.
So yeh…for the most part, they are not strictly enforced….but nice to make sure you have everything “noted”.
FIELD_REFFIELD_NAME, IS_CURRENCY_FIELD and IS_QUANTITY_FIELD
Now THIS is where things get interesting! These are the probably the most useful fields we can assign. For most all cases, you will use the FIELD_REFFIELD_NAME and then either use IS_CURRENCY_FIELD or IS_QUANTITY_FIELD. A field can not (should not!) be both a currency field and a quantity field. In a nutshell, this allows us to “reference” one field to another so that the actual value of the field will automatically be converted for display (external) and storage in the database (internal) without us having to call additional functions to do this for us.
FIELD_REFFIELD_NAME is our “reference field”. Think of it as saying “for our field, we need to refer to this other field in order to determine our value” (ie. to look for a conversion). However, even SAP does not always use this “correctly”. Here is example code for a standard SAP generic service….
However, if you look at method GET_TECHNICAL_FIELD_INFO from class CL_HRASR00_GENSERV_MAPPER (which is the only place this field gets checked), you see at line 66-76, it explicitly checks to see if either the IS_CURRENCY_FIELD or IS_QUANTITY_FIELD is true, otherwise it will CLEAR out the reference field name (ie. ignore it)…..soooo…..not sure how SAP’s own folks think it gets used. (haha)
(*there are MORE problems/BUGS with this code however, that I will cover later below)
SAP does the same kind of “reference field for currency/quantity” handling in the SAP_PA service. You can see this in class CL_HRASR00_PAITF_MAPPER method GET_TECHNICAL_FIELD_INFO. Around line 80, it loops though the fields mapped to SAP_PA (form_scenario_fields). Oddly enough, the “PA mapper” handles “reference field a little differently. For instance, say we mapped infotype 0008 annual salary and currency as follows:
In the GET_TECHNICAL_FIELD_INFO method as it loops through and finds our “annual salary” field, it does the following:
- Looks at the “mapping buffer table”, T5ASRFSCNPAMAP, to find how we mapped our “form field” to the PA structure field.
- From the “mapping”, it can “see” what the SAP_PA field is that we mapped to….in our case, “ANSAL”.
- It then can “lookup” the dictionary details (data element, type, etc) …you can see structure DFIES and see the definition. It very easily tells us our reference fields! (much easier than how we have to do this in our generic service).
- This definition describes the fields “reference field” and “reference field type”.
- It checks to see if a “reference field” is defined (and ours is) so then it goes back to the same “mapping buffer table” as in step #1 and finds that field.
- It gets the “PA fieldname” of our reference field (in our case, “ANCUR”) and again looks up the field’s dictionary definition (in DFIES again) to find the “data type”.
- If the reference field is data type “CUKY” (currency), then it sets our reference type as “C”. Otherwise, it is set as “Q” (quantity).
Here, you can see how the “PA mapper” builds the definition of our field.
In the Dispatcher (class CL_HRASR00_DISPATCHER method GET_FIELD_INFO), you can see where it calls either the PA Mapper or Generic Services Mapper to “get technical field info”.
It then takes a look at all of our collected (both SAP_PA and generic services) “technical field info” (around line 119) which eventually gets passed back to class CL_HRASR_PROCESS_UI_EVENTS and method ADD_REFFIELDS_TO_EXTERNAL_DATA. This one is a bit odd to me because it takes the actual currency value and makes that the reference field name (not to mention assuming it is always a “currency”).
Ok ok…back to our generic service and “GET_FIELD_INFO”. In my own generic service code, I have used it as follows as an example for using the infotype 0014 (Recurring Pay) “amount of payment”(BETRG) value and related “currency”(WAERS). Based on how the method GET_TECHNICAL_FIELD_INFO from class CL_HRASR00_GENSERV_MAPPER looks are out field definition, this is how it should be written:
You can see how I set my “amount” as the “reference field” on my “currency field”(WAERS) field and also marked my “currency field” as “is currency field” is true. However, this is COMPLETELY BACKWARDS from how SAP handled it in the “SAP PA MAPPER” as shown previously. If the generic service mapper actually followed how the SAP_PA Mapper does it, our code should look something like:
In other words, our “amount” field should have the corresponding “currency” field listed/defined as it’s “reference field”, and then on the “currency” field, it should have “is_currency_field” marked as true (“X”). This way, it would match with what SAP_PA returns to the Dispatcher when it’s “get technical field info” method is called. I have no idea why SAP does not do this!
Finally, to make matters even worse, the actual code in method GET_TECHNICAL_FIELD_INFO from class CL_HRASR00_GENSERV_MAPPER has a nasty little “bug”. If we do set up our field as having a “reference field” and “is currency field”…even doing it the “wrong” way….the code does set our “reference field type as “C”…..but will then set EVERY FIELD AFTER as “C” also….
This is because the code does not clear out the “structure” used:
You can see that badi_technical_field_info_wa is never reset/cleared. Line 64 just moves over the “corresponding fields” which “field_reffield_type” is not one of them. So once we set “field_reffield_type” as on line 68, if no other fields have a “reference field” as checked on line 66, then they just get the same value (“C”) set to them every time. This is why we can’t have nice things!!!! ARGGGGGGG!!!!
When you set this up, it “should” handle the input/output (external/internal) conversion for us auto-magic-ly. For example:
“The ANNUAL_SALARY field is associated with SALARY_CURRENCY field that you also have on the form. You defaulted the SALARY_CURRENCY field with currency JPY. If the amount is 15,00 JPY in the backend, it is actually shown as 1500 JPY. Similarly if you enter 900 JPY in the form, it should store as 9.00 JPY in the HR system.”
As awesome as all this appears, I know it is shocking, but this has not always worked so well (even in the SAP_PA service). There have been notes along the way. Honestly, because of the “bugs”, I often had to do my own “solution” for this much as SAP does say in screens like “Base Pay” where there are actually TWO fields for each currency value….one for display (a converted value) and the one that is actually stored (background/hidden one).
For the Adobe Interactive Forms & ISR days, there were some issues with this working correctly at first. However, SAP corrected it and provided this note:
1351143 – Conversion of Amounts based on Currency
FPM forms have had a “bug” with this not working correctly for some time now (hence I had to “workaround” it a lot!) , but thankfully it was corrected. I actually found out because when we did an upgrade, it actually started to work which then caused problems because I had been doing the conversion (in/out) myself! (haha) However, if you have not upgraded in a while, SAP put out this note for FPM forms back in April 2015:
2152011 – FPM Forms: Conversion of amounts based on currency not occurring
So now you should have a better understanding of what to define in GET_FIELD_INFO and why you should (other than not making me very angry if your code is ever handed over to me! haha). Like I said, this is one of the more often overlooked pieces of HCM P&F, so I thought it would be good to blog about (…and I may have just seen another developer’s code who left about half of his generic service fields undefined and “motivated” me to publish this one out! haha). As always, I hope you enjoyed it and even better if you found it useful. I will keep blogging if you keep reading. Till next time…
(*EDIT: added more detail around how SAP does not handle the currency/quantity conversion correctly in generic services and show the “bug”.)
Yes - 'people' are still working with this ..... Now with rendering in Fiori though..