HCM Processes & Forms: Taking control of your FPM form tables…by brute force! (haha)
Anyone in HCM P&F working with FPM forms will eventually stumble upon the requirement to provide a table of data that the user can view existing records and add new ones directly too. And almost equally as predictable, they will also be asked to make some fields read-only while allowing others to be directly editable. However, this is not as easy as it first sounds….but hopefully, this blog will help ease the time/sweat/cursing/frustration involved if you had to figure this out on your own.(haha)
For someone new to this, they are likely to think “oh, this is going to be easy. I will just set the UI attributes for the form field in configuration or in my own generic service. I will be done with this in a few minutes and can take a long lunch for the day!” Not so fast! You might want to hold up on those lunch plans because here lies the big “catch”.
For just single form fields, setting the UI properties (open for input, invisible, or read-only) directly in HCM P&F Design Time configuration or using the newer access to “ui_attributes” in our back-end services works wonderfully well. Our standard feeder class, CL_HRASR00_FPM_FEEDER, can read, understand this and make the settings perfectly for us. With multi-line fields (ie. tables), however, this is where it has a bit of a problem.
Suppose we have a table of wage type entries. We want to list existing wage types but only allow the user to edit the end date and amount (wage type and begin date should be read-only). We also want the user to be able to add a new “row” and be able to edit all fields. If we simply set our form field attributes the “usual” way, this is what we will get.
What we see it that it correctly made our “existing” records first two columns as “read only”, but it incorrectly applied this to our “new” row too, so the user can not even enter a new record correctly. What is worse is that if we completely handle adding the new row ourselves (custom back-end operation) and even implicitly set the UI attributes for all those “new” fields with the same index (meaning same row) to “open for input” (ie. “I” setting) , it STILL will ignore all of it and make our fields as “read only” in the first two columns. It does this because the feeder class has code that just looks at the FIRST occurrence of the field and whatever that field’s UI setting is becomes the setting for all other entries in that column (ie. it sets the “field usage”). In our case, it saw that the first use of “Wage Type” was read-only, so then, all other cells in that column get set to “read-only” (again, regardless if we actually set them independently by field index in ui_attributes in our custom service….I think this is a BUG in SAP code, but will leave it to them to decide/fix.)
So I set off to fix this on my own…and because I happened to be working on something very similar for a client at the time.(haha) First and foremost, I want to mention that it was Raja Sekhar Kuncham‘s blog FPM Forms Scripting (Continued)… that was the spark/motivation for this blog. He mentioned the usage of ET_FIELD_DESCRIPTION if setting multiple UI properties if only we could have access to these. I took it as a challenge. I tried many ways to get a handle/reference to the FPM feeder class of my UIBB list element, the good ol’ standard CL_HRASR00_FPM_FEEDER class, thinking that if I could just do that, it would be easy to change/set/inspect the ET_FIELD_DESCRIPTION structure. It was easy enough to get reference to the FPM framework instance itself (“FPM factory, get_instance”), so I thinking the feeder class surely had a simple “get instance” method too, right? No luck.
After poking around on SCN in FPM related threads and scouring Google searches, it seemed I had two options. One, I could do some things in the “GET_DATA” method to set UI properties, but it sounded like it would get too specific to one particular process and really require a completely custom feeder class. I did not want to go that route just yet. Second, I found several mentions of using the method “GET_DEFINITION” which…and I know how strange this will sound….is where you define how the form fields appear and what they can do (ie. search helps) up front (whereas in GET_DATA, you can override/change this later in a more dynamic way).
Again, because for single form field values the UI attribute settings already work correctly, I did not have to look at the FPM GUIBB “form” related “GET_DEFINITION” method. I focused only on the FPM GUIBB “List” code as this is what/how we display tables of information on our FPM forms. After debugging through this method several times, this did seem like a good spot. I decided I would do an “enhancement” in this class method to handle my own UI attribute settings. If you look at it, you will find it has two enhancement locations…one at the top and one at the bottom of the method.
I wanted my code to be executed after everything else had been set and done. Furthermore, instead of having code in there directly for all my processes, form fields, etc. that I would have to update/change as I used this more and more, I instead created a custom class that I pass the “et_field_description” structure to in order to make changes and pass back. In that way, I simply only have to change my custom class code as needed. Scroll to the bottom and create your own enhancement (I will leave it to you to figure “how” to do this if you are not already familiar with it.)
EDIT: I found a neat trick!….and changed my method call a bit from what is shown above for my custom class to handle this. I found that I can pass in the current “form scenario” and “form scenario step” as well, so that means I can control fields even on a more granular level. My class can now have different internal method calls based on the forms scenario used! Pretty slick! *HINT: Look at passing in/exporting values “A_FORM_SCENARIO” and “A_FORM_SCENARIO_STAGE” to your own method.
One thing you will notice in et_field_description is that many of the UI related fields (viability, read only, mandatory, tooltip, etc) will have a corresponding “reference” field. What this means is that you “could” for instance set a field as “read only” by marking the “read only” field as “X”. Or you could say, “I am not sure how this field will be set, so I want another field to decide this at run time” and THAT is what the “reference” field is for. You are, in effect, naming another field that will decide this for you dynamically. Take note, though, you would never set both….that is, I would not have a field set as “read only” but also having a “read only reference field” set too. The second, and likely foremost, important piece of this is that whatever field we use as a “reference field” must have it’s setting for “technical field” in et_field_description as “X”. This tells us that THIS field is in fact a “reference field” and should NOT be considered for having UI settings of it’s own. The nice thing here too is that we can use this same “reference field” for any number of other fields. For example, suppose we had 4 fields on our form and our reference field too.
WAGE_TYPE WAGE_AMOUNT WAGE_BEGIN_DATE WAGE_END_DATE WAGE_REFERENCE_FIELD
And in ET_FIELD_DESCRIPTION, we had something like this:
|Field Name||Technical Field||Read Only Reference|
Then at run time, if we set the WAGE_REFERENCE_FIELD’s value to “X”, it would then cause all the related fields to appear as “read only” (greyed out). Pretty nice to control several fields with one field setting! For tables, we could decide this “per index” even so that it affect a row but not all rows. (remember in HCM P&F “tables” that all fields in each column with the same index represent a table row).
Now, back to our own class, we can handle setting/controlling UI elements much easier. I have methods for each type of UIBB “get definition” possible (form and list) as their “et_field_description” structure is slightly different. Of course, you can probably come up with all kinds of more clever ways to do this, but I will just make the changes directly. You can see here how I set up the “technical field” and “reference field” relations. I am telling it that “for the wage type and begin date fields, I want their read-only setting to be determined by another field called wagetype_ref_readonly at run time.”
EDIT: This changed for me slightly. I found that after I did this and went back to the FLUID designer, it did NOT like that I had set the value on my “technical field”. So, setting the technical field to X (read only) or “” (open for input) is now handled in my generic service. I do STILL handle assigning my technical field to my form field like this however in my custom “extended” class. Also, in FLUID, you must add the “technical fields” to your table layout (ie. as columns….easy to put them next to the column they are a reference for) and then select the “Visibility” to “Not Visible” (instead of “Determined by Feeder Class (Default)”.
The next big part of this is that we also make settings in our custom back-end generic service. Over in the service in our “initialize” method as we fill our wage type values, we just make sure we set these values for our “reference” field and make sure it’s index matches the correct row (ie. make it match our “wagetype” row). In our case, if we have an existing wage type record, we want to make the field “read only”, so the value then for our “reference” field is “X”.
Similarly, in our “do operations” method, I am catching if the user event for “add row” is triggered using the same way I mention in another blog. I will also mention that I made a custom user event and do not use the standard “USER_EVENT_ADD_ROW “ event so that I can “catch” it in my method (the standard event would not trigger my “do operations”). You can see here that I clear the “reference field” value so that means our field will be open for input.
After all of this, if I run my process again and form is displayed, this is what we now see:
You can see that for the “existing” entries, the first two cells of each row “read-only”. However, all “new” rows are now correctly completely open for input.
Now, if we take this a little further, using the fields now exposed to us in our class, using many of the various “reference fields” in et_field_description and more, we can have all kinds of fun controlling just about everything on our table as you see here:
We have a custom column header now, a custom tooltip (we can make the tooltips different for each row or one generic message for all rows…think about that! Wooo whoo!!!! haha), and again, we control the cells on thefirst two columns being read-only or not. Now, you should understand (and have the power to control!) just about any and every thing about your FPM list/table UI display.
You can look through the structure FPMGB_S_LISTFIELD_DESCR for more. I will leave a few of them up to you to play with….hey, I can’t be having all the fun over here! (haha) As always, I hope this has helped, and I will keep churning them out if you keep reading them. Till next time…