cancel
Showing results for 
Search instead for 
Did you mean: 

Accessing FPM_OIF_COMPONENT from an "inner" view to disable a button

matteo_montalto
Contributor
0 Kudos

Hi all gurus,

please forgive the question, I'm not that expert on WD and found no docs related to the question.

This is the issue I'm facing now: I'm working in SRM7 with a standard wd which is triggered when the user decide to create a Purchase Order from scratch, manually.

The WD is /SAPSRM/WDC_UI_PO_PS, view V_PO_PS ; on top of this view, in the browser, a toolbar with two buttons is available (button Continue and button Close).

These two buttons are part of FPM_OIF_COMPONENT, view INITIAL_SCREEN.

Now; my desiderata is to disable/enable the START button (ID: BTN_START) on the basis of a certain value in a field. In detail, START should be enabled only if COMPCODE (which is a dropdownByKey field in V_PO_PS) is correctly filled.

Since this should be a dynamic operation triggered by an event onSelect on this dropdown, I guess I should be able to access via code to the FPM_OIF_COMPONENT at runtime. Can anyone provide me a simple code pattern to access this component ? I tried via debug to access these buttons starting from the view but it seems to me they cannot be reached.. true?

Thanks again for your help!

Edit: I entered the correct english name of the button as well as the button ID.

I tried also as follow to understand how to refer to this button:in WDDOINIT post-exit I wrote the following code:

DATA: GO_FPM type ref to if_fpm.
DATA: GO_FPM_OIF TYPE REF TO IF_FPM_CNR_OIF.

  go_fpm = cl_fpm_factory=>get_instance( ).
  go_fpm_oif ?= go_fpm->get_service( iv_service_key = cl_fpm_service_manager=>gc_key_cnr_oif ).

  CALL METHOD go_fpm_oif->get_buttons
*  EXPORTING
*    iv_variant_id =
  IMPORTING
    et_buttons    = buttons_OIF  .

Buttons_OIF contains 55 elements, all from the variant_id PO_PUR, but there's no BTN_START in it

Edited by: Matteo Montalto on Feb 14, 2011 12:38 PM

Accepted Solutions (1)

Accepted Solutions (1)

Former Member
0 Kudos

Hallo Matteo,

You can do something like this ( i doubt if you can get this working in initial screen )

wd_this->go_fpm_oif->define_button(

iv_function = if_fpm_constants=>gc_button-activation_function

iv_element_id = 'BTN_START'

iv_enabled = abap_false ).

But i would advice you not to do that. Instead return ev_result as failed.

method process_event .

* When leaving the initial screen we need to check the selection parameter and read all the required data.
  if io_event->mv_event_id = if_fpm_constants=>gc_event-leave_initial_screen.
" do your checking and if condition failed based on your dropdown by key
     wd_this->mo_fpm->mo_message_manager->report_message( iv_message_text   = lv_error_text
                                                           iv_severity       = if_fpm_message_manager=>gc_severity_error
                                                           io_element        = <Node element>
                                                           iv_attribute_name = 'attribute'
                                                           io_component      = me ).
*     ... and stay on the initial screen
      ev_result = if_fpm_constants=>gc_event_result-failed.
endif.

See examples in package apb_fpm, sub package apb_fpm_demo, component FPM_DEMO_FLIGHT_OVERVIEW.

matteo_montalto
Contributor
0 Kudos

>

> Hallo Matteo,

>

> You can do something like this ( i doubt if you can get this working in initial screen )

>

> wd_this->go_fpm_oif->define_button(

> iv_function = if_fpm_constants=>gc_button-activation_function

> iv_element_id = 'BTN_START'

> iv_enabled = abap_false ).

Hi baskaran and thanks for your help,

the above sketch of code does not work, or at least, it doesn't work in WDDOINIT.

We can consider another possibility, which is, if I understood it right, the way you suggested as alternative:

>

> But i would advice you not to do that. Instead return ev_result as failed.

>

>

method process_event .
> 
> * When leaving the initial screen we need to check the selection parameter and read all the required data.
>   if io_event->mv_event_id = if_fpm_constants=>gc_event-leave_initial_screen.
> " do your checking and if condition failed based on your dropdown by key
>      wd_this->mo_fpm->mo_message_manager->report_message( iv_message_text   = lv_error_text
>                                                            iv_severity       = if_fpm_message_manager=>gc_severity_error
>                                                            io_element        = <Node element>
>                                                            iv_attribute_name = 'attribute'
>                                                            io_component      = me ).
> *     ... and stay on the initial screen
>       ev_result = if_fpm_constants=>gc_event_result-failed.
> endif.

if I understood it right, you say that we could raise an error, blocking message when clicking the BTN_START button. That could be a nice solution too.

Actually I did not understand where I should code the above sketch, I mean, probably in FPM_OIF_COMPONENT WD, for the view INITIAL_SCREEN?

My question is ... how can we be sure that this one is used only in the web dynpro we are working on? As, as I explained in the first post, we deal with the /SAPSRM/WDC_UI_PO_PS web dynpro (view V_PO_PS) and in this WD, from SE80, I don't see any FPM button. obviously. Can you please help me understanding this point? Thanks in advance.

Former Member
0 Kudos

Hi Matteo,

> Actually I did not understand where I should code the above sketch, I mean, probably in FPM_OIF_COMPONENT WD, for the view INITIAL_SCREEN?

/SAPSRM/WDC_UI_PO_PS has a method process_event, you can code the above code snippet there.

My question is ... how can we be sure that this one is used only in the web dynpro we are working on? As, as I explained in the first post, we deal with the /SAPSRM/WDC_UI_PO_PS web dynpro (view V_PO_PS) and in this WD, from SE80, I don't see any FPM button.

The buttons are all defined in the FPM configuration. So you wouldnt see it in your view V_PO_PS.

matteo_montalto
Contributor
0 Kudos

Hi Baskaran, thanks for your help and patience, as you probably have noticed I'm not a guru on WD ...

So... I found out that in that view, once START_BTN is pressed, the following method is executed:

ONACTIONSTART_PO_OIF

and this has the following interface:

WDEVENT Importing Type ref to CL_WD_CUSTOM_EVENT

Now... I tried to check for the value we need to continue, that works. Also, the error message works BUT it is not blocking. As you see the interface doesn't provide any ev_result table, so that assignment is not checked and in case of error, it does not stay in the initial screen. The result by now is that the flow passes to the next screen, with my error message filled in the message area.

Guess we're really close to the solution...

Former Member
0 Kudos

Hallo Matteo,

i think there is some misunderstanding going on.

> So... I found out that in that view, once START_BTN is pressed, the following method is executed:

> ONACTIONSTART_PO_OIF

This means that the start button is not from the FPM which appears in initial screen.

You have to code my code snippet in the process_event method in the component controller on the event

case lv_event_id.

when if_fpm_constants=>gc_event-leave_initial_screen.

If this is not the case then we are talking about two different things.

matteo_montalto
Contributor
0 Kudos

Ok Baskaran, I got it, I was working on the view's methods instead of component controller's one.

Actually the solution you provided works for me; the only problem is now, how to get values from the "main" view. Component controller's context is empty while the view's context contains the attribute EXTERNAL_NUMBER which I should check in order to see if it's filled or not.

I was trying via enhancement to create the same attribute for the component controller's Context, but then I have no idea on how to "map" this new attribute so that becomes properly filled w.r.t. EXTERNAL_NUMBER in the view's context.

In the CONTEXT section in se80 for the view I have:

- the view's context on the left (containing the attribute EXTERNAL_NUMBER as a direct root attribute);

- the component controller's cotext on the right (which contains the attribute EXTERNAL_NUMBER which I created via enhancement).

When I try to drag & drop EXTERNAL_NUMBER from the view's context to the same attribute under the component controller's context, the system tells that it's not possible to specify a mapping between attributes (only nodes). But since EXTERNAL_NUMBER is by standard an attribute of the root in the view's context, how can I overcome this issue?

Thanks again Baskaran!

Matteo

Former Member
0 Kudos

Hallo Matteo,

Yes, it is not possible to map a context attribute directly under root node.

what you can do is to create a context node in component controller.

ext_nr_enh cardinality 1:1 and define a attribute external_number_enh of type external_number.

Now you can map this node ext_nr_enh to the main_view.

you can copy the data from external_number to external_number_enh.

from component controller, you can check the value of external_number_enh.

I hope this works for you.

matteo_montalto
Contributor
0 Kudos

Hi Baskaran,

many thanks to you, that worked and the issue is now completely solved. Anyway, got still a question for you (that's why I did not close yet the message):

- I created a node in the context of the view, say NODE_ENH, with the atttribute I need, EXTERNAL_NUMBER_ENH. This is properly filled in the method triggered when the user changes the "external number field" value. I created then via enhancement the same node in the component controller's context and bind them from the view. That worked.

BUT...

...that "process_event" method in ComponentController is triggered by many other views (not only in manual creation, but also in creation from a Shopping Cart, or when you click on a PO number from the search results table, and so on).

So I had to find a way to identify that we're in case on PO manual creation process. To do so, i simply added an attribute MANUAL_CREATION (type abap_bool) to both NODE_ENH in view and component controller, and in the WDDOINIT method of the view, I set it to 'X'.

Then in PROCESS_EVENT I coded as follows:

DATA: go_fpm TYPE REF TO if_fpm.
  DATA: lo_node_enh  TYPE REF TO if_wd_context_node.
  DATA: lv_bukrs TYPE string.
  DATA: lv_manual TYPE abap_bool.

  go_fpm = cl_fpm_factory=>get_instance( ).
  CALL METHOD wd_context->get_child_node
    EXPORTING
      name       = 'NODE_ENH'
    RECEIVING
      child_node = lo_node_enh.
  CALL METHOD lo_node_enh->get_attribute
    EXPORTING
*        index  = USE_LEAD_SELECTION
      name   = 'MANUAL_CREATION'
    IMPORTING
      value  = lv_manual
      .
  CHECK lv_manual EQ abap_true.

* When leaving the initial screen we need to check the selection parameter and read all the required data.
  IF io_event->mv_event_id = if_fpm_constants=>gc_event-leave_initial_screen.
....

It works (original PROCESS_EVENT method was empty)... but I wonder if there's a "cleaner" way to obtain the same result or if you find any drawback in this approach.

Thanks again.

Matteo

Former Member
0 Kudos

Hi,

I think you have solved it good.

There are other ways , i am not sure if they are clean way :).

1.if_fpm has a method GET_RUNTIME_INFO which returns a parameter config_id. You can use this to restrict to your PO application.

2.Process_event has a importing parameter it_interface_views. You can read this table with index=1 would give you a current interfaceView name. You can check this one to see if it is your interfaceView such as defined in the FPM configuration. iam not sure if this going to work for you.

Anyways, as i already said. Your solution is fine. You can try the option 1 which might result in less work.

matteo_montalto
Contributor
0 Kudos

Hi Baskaran,

Thanks for your precious help. Everything solved now

matteo_montalto
Contributor
0 Kudos

Sorry Baskaran,

I found a side-effect on the solution you purposed, maybe you can give me an help...

That's the scenario: suppose the user press START button when the dropdown selection is empty -> on the basis of the checks you described above, an error message is thrown and the process "stay" on the current screen.

Now, the user selects a valid value from the dropdown (which should be used to create the follow-on document).

What we are getting in such a scenario is that the value chosen from the dropdown isn't transferred correctly to the follow-on document.

This is what I found debugging:

- when the user press START button without dropdown selection, an instance of mo_bo_mapper (type /SAPSRM/IF_CLL_BOM_PO) is retrieved (actually created, because it's the first time).

That instance has the dropdown-related field filled with blank, no surprises here.

- after a valid dropdown selection, START is pressed again. But now, since mo_bo_mapper already exists, it is used and not updated.

As a consequence, the selected value is not taken over the following document.

So basically the point is: how can I destroy the mo_bo_mapper object in case of error from the PROCESS_EVENT method?

I tried a bit but seems that all the related methods are protected or private...

Thanks again, let me know in case I can open a new thread on the specific argument.

Former Member
0 Kudos

Hallo Matteo,

I have no idea how in your application /SAPSRM/IF_CLL_BOM_PO mapper class is constructed. There is no destructor for the Objects.

i would try to find out the place where /SAPSRM/IF_CLL_BOM_PO is created and contruct a if .. endif construct. create this instance only when valid value is selected.

matteo_montalto
Contributor
0 Kudos

Hi Baskaran,

The piece of code in which the mapper is created is absolutely standard: class /SAPSRM/CL_FPM_OVRIDE_OIF_PO, method INIT_OBJECTS. Here:

" Create BO/XO mapper
  mo_bo_mapper ?= mo_task_container->get_bom_po(  ).

Which triggers the method get_bom_po( ) in which:

method /SAPSRM/IF_CLL_TASK_CONTAINER~GET_BOM_PO.
  DATA: lo_wd_map_factory   TYPE REF TO /sapsrm/if_ch_wd_map_factory
         .

  IF mo_bo_mapper IS INITIAL.
* get BO-Mapper of PO
 lo_wd_map_factory = /sapsrm/cl_ch_wd_map_factory=>/sapsrm/if_ch_wd_map_factory~get_instance( ). "getFactory
    mo_bo_mapper     ?= lo_wd_map_factory->create_po_mapper( io_task_container = me  ).
  ENDIF.
  ro_bom_po ?= mo_bo_mapper.

endmethod.

As we can see if the mapper has been previously created, then it is considered (and NOT updated) so our check doesn't work, or better... It works since does not allow the user to leave the dropdown selection empty, but after that, no selected value is brought over the following document.

I'd prefer not to modify the standard code to manage the task... Isn't there any way to "rollback" in case of errors in your opinion?

matteo_montalto
Contributor
0 Kudos

Actually, I've seen that the view itself has a "cross" method for the componentcontroller, which is FPM_START_HANDLE (event) and has Controller COMPONENTCONTROLLER.

I tried to port my sketch of code (with error raising) but anyway: when the error occours, the message is thrown but the event/process still goes over. No way to block the process just throwing an error message.

I also tried to put the dropdown field selection obligatory, however it seems not to be checked as the START button is always active.

Edited by: Matteo Montalto on Mar 7, 2011 5:47 PM

Former Member
0 Kudos

Hi,

Matteo, i have no idea about your application structure and hence it is very difficult to provide/guide you for a solution.

From FPM perspective i have 3 other suggestion.

1.if_fpm_oif has cancel_event( ).

i do not know if you have if_fpm_oif_conf_exit implemented in your component. This interface has a method override_event_oif and that has a importing parameter io_oif of type if_fpm_oif.

2.Change the current event to fpm_start or fpm_start_over

io_oif->mo_event = cl_fpm_event=>create_by_id( if_fpm_constants=>gc_event_start ).

or

io_oif->mo_event = cl_fpm_event=>create_by_id( if_fpm_constants=>gc_event_star_overt ).

3.Fire new FPM event

data: lo_fpm type ref to if_fpm,

lb_ddbk_ok type wdy_boolean.

  • Check event id

if lv_event_id = if_fpm_constants=>leave_initial_screen.

lo_fpm = cl_fpm_factory=>get_instance( ).

  • Determine if your DDBK is selected, navigate directly to the main screen

" lb_ddbk_ok = <set the value abap_true /false>

if lb_ddbk_ok <> abap_truel.

lo_fpm->raise_event_by_id(

if_fpm_constants=>gc_event_start ).

endif.

you can also try with

lo_fpm->raise_event_by_id(

if_fpm_constants=>gc_event_start_over ).

If these are all not working, then please get a good consultant who can help you with this. Success !!

matteo_montalto
Contributor
0 Kudos

Thanks Baskaran, solved!

I basically moved my code to FPM_START method so that under certain conditions the error message is thrown and the event is not even raised.

Thanks again for your support.

Answers (0)