How to get updated HTML radio button in Workflow Inbox? - workflow

Since I'm so new for workflow, now I'm stuck at on how to get updated radio button of HTML table in Workflow inbox when click decision button.
STEP 1: ln HTML table, the user can approve or reject WBS by checked radio
STEP 2: When user click confirm, then the workflow will update status for each WBS that shown in the table.
Question:
On STEP 2 - how can I get the HTML text which updated by user action.
For now I've enhanced SAPEVENTS and using 'SAP_WAPI_READ_CONTAINER' to get HTML container.
However, after check the HTML code, the HTML table is not update to user action.

Here's a minimal program which demonstrates how to get the values of the radio buttons. If the user presses Approve on the first line and Reject on the second line, and presses the button Confirm, the method on_sapevent gets group[1]=approve&group[2]=reject.
(works in something like ABAP 7.40 SP8)
REPORT zdemo.
CLASS lcl_app DEFINITION.
PUBLIC SECTION.
METHODS at_selection_screen_output.
METHODS on_sapevent FOR EVENT sapevent OF cl_gui_html_viewer
IMPORTING action frame getdata postdata query_table.
PRIVATE SECTION.
DATA o_html TYPE REF TO cl_gui_html_viewer.
ENDCLASS.
CLASS lcl_app IMPLEMENTATION.
METHOD at_selection_screen_output.
DATA: l_url TYPE cndp_url,
l_text TYPE string.
IF o_html IS NOT BOUND.
l_text = '<body><form id="form" method="POST" action="SAPEVENT:submit">'
&& REDUCE string( INIT t = `` FOR I = 1 WHILE i <= 2
NEXT t = t && |<div><fieldset id="group{ i }">|
&& |<input type="radio" value="approve" name="group[{ i }]"> Approve |
&& |<input type="radio" value="reject" name="group[{ i }]"> Reject|
&& |</fieldset></div>| )
&& '<p><button type= "submit">Confirm</button></p>'
&& '</form></body>'.
o_html = NEW #( parent = cl_gui_container=>screen0 ).
SET HANDLER on_sapevent FOR o_html.
o_html->set_registered_events( events = value #( ( eventid = o_html->m_id_sapevent ) ) ).
DATA(lt_text) = cl_bcs_convert=>string_to_soli( l_text ).
o_html->load_data(
EXPORTING type = 'text' subtype = 'html' size = strlen( l_text )
IMPORTING assigned_url = l_url
CHANGING data_table = lt_text ).
o_html->show_url( EXPORTING url = l_url ).
ENDIF.
ENDMETHOD.
METHOD on_sapevent.
CONCATENATE LINES OF postdata INTO data(l_post_data) RESPECTING BLANKS.
MESSAGE l_post_data TYPE 'I'.
ENDMETHOD.
ENDCLASS.
PARAMETERS dummy.
DATA go_app TYPE REF TO lcl_app.
LOAD-OF-PROGRAM.
CREATE OBJECT go_app.
AT SELECTION-SCREEN OUTPUT.
go_app->at_selection_screen_output( ).

Related

SAP Custom Workflow is not working (LPOR missing)

I created my first custom OO Workflow.
I created the class and added the interface IF_WORKFLOW.
I implemented the methods LPOR and FIND_BY_LPOR of interface BI_PERSISTENT.
The value of INSTID should be the Workitem ID.
I just have one activity (dialog) in my workflow. When I'm testing the workflow, an error appears.
Error-Code WL-821 - Dereferencing NULL-Reference.
I created a breakpoint in my custom FIND_BY_LPOR method. There is a CHECK for LPOR-INSTID is NOT INITIAL.
Unfortunately my LPOR-INSTID is always INITIAL. LPOR-CATID and LPOR-TYPEID are filled.
Is there something I have to customize?
Code:
CONSTRUCTOR (When is the constructor called, I can't get in while debugging?)
m_workitem_id = i_wi_id.
ms_lpor-catid = swfco_objtype_cl.
ms_lpor-typeid = mc_objtype.
ms_lpor-instid = i_wi_id.
LPOR
result = ms_lpor.
FIND_BY_LPOR
DATA: ls_key TYPE zs_wf_key,
ls_instance TYPE ty_instance.
CLEAR: ls_key, ls_instance.
CHECK lpor-instid IS NOT INITIAL. "HERE ITS EXITING, BECAUSE INSTID = INITIAL
READ TABLE mt_instances WITH KEY id = lpor-instid
INTO ls_instance.
IF sy-subrc NE 0.
TRY .
ls_key = lpor-instid.
CREATE OBJECT ls_instance-instance TYPE (lpor-typeid)
EXPORTING
is_key = ls_key.
CATCH cx_bo_error cx_root
RETURN.
ENDTRY.
ls_instance-id = lpor-instid.
APPEND ls_instance TO mt_instances.
ENDIF.
result ?= ls_instance-instance.

How to modify double click on ALV event?

Hi guys!
I found a program on the internet that runs row details when I double-click the 'CARRID' cell on ALV.
How to rewrite this code so that the details are displayed no matter what cell in the table I click, not only in 'CARRID'?
CLASS lcl_handle_dc DEFINITION.
PUBLIC SECTION.
METHODS double_click
FOR EVENT double_click OF if_salv_gui_table_display_opt
IMPORTING ev_field_name eo_row_data.
ENDCLASS.
CLASS lcl_handle_dc IMPLEMENTATION.
METHOD double_click.
DATA: ls_sflight TYPE sflight.
CHECK ev_field_name = 'CARRID'.
* read the row data
eo_row_data->get_row_data(
EXPORTING
iv_request_type = if_salv_gui_selection_ida=>cs_request_type–all_fields
IMPORTING
es_row = ls_sflight ).
* Display the row data
cl_salv_ida_show_data_row=>display( iv_text = 'Flight Row Info' is_data = ls_sflight ).
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA: lr_salv TYPE REF TO if_salv_gui_table_ida,
lr_handle TYPE REF TO lcl_handle_dc.
cl_salv_gui_table_ida=>create(
EXPORTING
iv_table_name = 'SFLIGHT'
RECEIVING
ro_alv_gui_table_ida = lr_salv ).
DATA(lr_disp) = lr_salv->display_options( ).
* Enable double click
lr_disp->enable_double_click( ).
CREATE OBJECT lr_handle.
SET HANDLER lr_handle->double_click FOR ALL INSTANCES.
* Display ALV
lr_salv->fullscreen( )->display( ).
Remove the below line of code.
CHECK ev_field_name = 'CARRID'.
The CHECK statement ensures that the logical expression that follows evaluates to abap_true before continuing executing the next statements.

How to write directive to allow input field with type= text to allow 'words'?

enter image description here I am using ngxdatatable in reactive forms where user can update all the fields in the table. i have three fields/columns for e.g fields a,b and c and they are dynamic as created below:
-- code for ngxdatatable columns
<div [formgroup]= this.form[rowindex]
<ngx-datatable-column *ngFor="let column of columns; let i = index;" name="{{column.name}}" prop="{{column.prop}}">
<ng-template #buttonsTemplate let-row="row" let-value="value" ngx-datatable-cell-template>
<input type =text isReadonly ="isreadonly">
</ng-template>
</ngx-datatable-column></div>
I am facing challenge in defining my validation for field B/column B,
1. **field b** can be autocalculated as number from field a and field c , **b = a+c**, but
user can't enter number manually in field b , on manual input only few strings are allowed like
'test', 'ok', 'any'. how can I achieve this ?
2. I tried to implement it via custom validation which works fine during manual input but it is failing because field/col B is autocalculated it shows error for number which is a valid scenario , as validation is only to be shown when user tries to input manually.
> this.fb.builder{ field a = validtion.Required,
> validation.patter(_number) field c=validtion.Required,
> validation.patter(_number) field b = customValidator }
>
>
> export function customValidator(c:abstract control) :{key:value}{
> if(c.value=='test' || c.value==''any || c.value == 'ok'} return null
>
> else {allowed:false} }
3. I thought of trying to achieve it with keydown event using directive , but not sure how to write a directive which allows words 'any', 'ok', 'test' and not anything else.**please suggest how can i write the directive for this as I am new to angular.**
4. Please suggest if you think any better approach can be used.

ABAP: Event CONTEXT_MENU_SELECT of CL_SIMPLE_TREE_MODEL - can't fire it

I have an instance of CL_SIMPLE_TREE_MODEL, I managed to trigger and handle the CONTEXT_MENU_REQUEST event, and I build my context menu.
I added my functions the way I wanted.
Problem is, when I select one of the options from my context menu, nothing happens. In other words, the program flow doesn't go inside the handler for CONTEXT_MENU_SELECT.
I'm of course assuming this event is fired when I click on a function in the context menu.
I found official documentation, but only for the "default context menu" that you access with Shift+F10, that needs certain subroutines in the program in order to fire.
What I did:
I did define and implement a method that is a handler for that event.
I did set the handler for the event at the same place I set the handler for the CONTEXT_MENU_REQUEST event
I did NOT register the event with the SET_REGISTERED_EVENTS because there is NO ID defined in the class attributes for that event - such as there was for the CONTEXT_MENU_REQUEST event.
The code:
REPORT.
CLASS lcl_tree_handler DEFINITION.
PUBLIC SECTION.
METHODS:
pbo,
on_ctx_menu_request FOR EVENT node_context_menu_request OF cl_simple_tree_model
IMPORTING node_key menu sender, "TYPE TM_NODEKEY CL_CTMENU
on_ctx_menu_select FOR EVENT node_context_menu_select OF cl_simple_tree_model
IMPORTING node_key fcode. "TYPE TM_NODEKEY SY-UCOMM
DATA:
po_tree_model TYPE REF TO cl_simple_tree_model,
gt_tree TYPE TABLE OF treemsnodt,
control TYPE REF TO cl_gui_control.
ENDCLASS.
CLASS lcl_tree_handler IMPLEMENTATION.
METHOD pbo.
DATA: lt_events TYPE cntl_simple_events,
ls_event TYPE cntl_simple_event.
FIELD-SYMBOLS <gs_tree> TYPE treemsnodt.
CHECK po_tree_model IS NOT BOUND.
CREATE OBJECT po_tree_model
EXPORTING
node_selection_mode = po_tree_model->node_sel_mode_single.
APPEND INITIAL LINE TO gt_tree ASSIGNING <gs_tree>.
<gs_tree>-node_key = 'Node key 1'.
<gs_tree>-text = 'First node'.
<gs_tree>-isfolder = 'X'.
APPEND INITIAL LINE TO gt_tree ASSIGNING <gs_tree>.
<gs_tree>-node_key = 'Node key 2'.
<gs_tree>-relatkey = 'Node key 1'.
<gs_tree>-relatship = cl_tree_model=>relat_last_child.
<gs_tree>-text = 'First child'.
po_tree_model->add_nodes(
node_table = gt_tree ).
ls_event-eventid = cl_simple_tree_model=>eventid_node_context_menu_req.
ls_event-appl_event = 'X'. "tried with space too
APPEND ls_event TO lt_events.
CALL METHOD po_tree_model->set_registered_events
EXPORTING
events = lt_events.
SET HANDLER on_ctx_menu_request FOR po_tree_model.
SET HANDLER on_ctx_menu_select FOR po_tree_model.
po_tree_model->create_tree_control(
EXPORTING
parent = cl_gui_container=>screen0
IMPORTING
control = control ).
ENDMETHOD.
METHOD on_ctx_menu_request. "I initialize the context menu object here.
DATA: lt_chidren_keys TYPE treemnotab,
ls_child_key TYPE tm_nodekey,
lv_text TYPE gui_text.
CALL METHOD sender->node_get_children
EXPORTING
node_key = node_key
IMPORTING
node_key_table = lt_chidren_keys
EXCEPTIONS
OTHERS = 2.
LOOP AT lt_chidren_keys INTO ls_child_key.
lv_text = ls_child_key.
CALL METHOD menu->add_function
EXPORTING
fcode = 'ONE'
text = lv_text
ftype = 'B'.
ENDLOOP.
menu->add_separator( ).
CALL METHOD menu->add_function
EXPORTING
fcode = 'ALL'
text = 'All the work groups'
ftype = 'W'.
ENDMETHOD.
METHOD on_ctx_menu_select.
BREAK-POINT. "tried actual code here too.
ENDMETHOD.
ENDCLASS.
DATA: go_tree_handler TYPE REF TO lcl_tree_handler.
PARAMETERS dummy.
INITIALIZATION.
CREATE OBJECT go_tree_handler.
AT SELECTION-SCREEN OUTPUT.
go_tree_handler->pbo( ).
AT SELECTION-SCREEN ON EXIT-COMMAND.
go_tree_handler->control->free( ).
The tree is displayed, on right click the context menu appears.
But nothing fires when I chose a menu item. Am I missing something ?
The function types you specify (ftype = 'W' and 'B') are not supported (check the fixed values of the underlying domain CUA_FUNTYP). In that case, nothing happens.
The classic solution is to use ftype = ' ' (normal function):
LOOP AT lt_chidren_keys INTO ls_child_key.
lv_text = ls_child_key.
CALL METHOD menu->add_function
EXPORTING
fcode = 'ONE'
text = lv_text
ftype = ' '.
ENDLOOP.
menu->add_separator( ).
CALL METHOD menu->add_function
EXPORTING
fcode = 'ALL'
text = 'All the work groups'
ftype = ' '.
The possible ftype values are (source: domain CUA_FUNTYP):
' ' : Normal function
'H' : Help function (PROCESS ON HELP REQUEST)
'S' : System function (handled directly by DYNP)
'T' : Transaction call (LEAVE TO TRANSACTION)
'E' : Access modules for 'AT EXIT COMMAND' -> /E as prefix
'I' : Include menu (replaced at runtime - not supported)
'N' : 'AT EXIT COMMAND' Function, > DYNP > /N as Prefix
Remark: the code in the first version of the question was missing the registering of the second event handler SET HANDLER go_tree_handler->on_ctx_menu_select FOR po_tree_model. (now it's okay)

How to populate zend form field using session?

I am using sessions to populate a multi select box with options in my Zend application.
The user selects one or more options and fills in other fields on the form and then submits. If the user didn't select all of the options in the multi select then the form is displayed again but the multi select only has the options that the user did not select the last time. This process goes on until there are no more options from the multi select left to process.
Here is the code I use to get rid of the options that have already been processed so that they are not used to populate the multi select box:
if($form_successful){
// TODO remove $post['keyword_names'] (i.e. already processed) from $keyword_names (that come from $_SESSION)
$keyword_names = array_diff($keyword_names, $post['keyword_names']);
print_r($keyword_names);
if(is_array($keyword_names) && !empty($keyword_names)){
// save updated $keyword_names into $_SESSION['workflow1']
$session = new Zend_Session_Namespace('workflow1');
$session->keyword_names = $keyword_names;
// set flag to false so that we display form again
$form_successful = false;
}else{ // all keywords have been assigned
// go to next step
$this->_redirect('/workflow-1/step-'.($step+1).'/');
}
}
print_r($keyword_names); displays the correct options, however when the form is loaded when the user submits, the multi select displays the options that were there from the begining ie the options the user has just selected and submitted are not being taken out of the multi select, it is only when the user submits the form again then the multi select box updates.
Appreciate the help.
Solved the issue by making use of URL parameters. Here is the code (might differ a lot from what I posted first because some big changes were made):
// after successful form submission
if($form_successful){
// remove $post['keyword_names'] (i.e. already processed) from $keyword_names (that come from $_SESSION)
$keyword_names = array_diff($keyword_names, $post['keyword_names']);
// save remaining $keyword_names into $_SESSION['workflow1']
$session = new Zend_Session_Namespace('workflow1');
$session->keyword_names = $keyword_names;
if(is_array($keyword_names) && !empty($keyword_names)){
// redirect to the same step again - to ensure that the form will reflect (in select lists) newly created AdGroup and/or Campaign
// GET parameteres ($params_array) provide a way to remember user's choice
$params_array = array();
if(!empty($post['match_type_id'])){
$params_array['match_type_id'] = $post['match_type_id'];
}
if(!empty($post['with_permutations'])){
$params_array['with_permutations'] = $post['with_permutations'];
}
if(!empty($ad_group_id)){
$params_array['ad_group_id'] = $ad_group_id;
}
$this_step_url = UrlUtils::assemble('', $this->getRequest()->getActionName(), $this->getRequest()->getControllerName(), $this->getRequest()->getModuleName(), $params_array);
$this->_redirect($this_step_url);
}else{ // all keywords have been assigned
// go to next step
$this->_redirect('/workflow-1/step-'.($step+1).'/');
}
}
So you don't have any code about Zend_Form object here. How do you populate the form element? If you post your class code which extends Zend_Form (or any other code dials with your form) then I may help. But in any case you can populate your multiselectbox with setMultiOptions() method or addMultiOption() for each item in multiselectbox.