Requirement and why I used ALV tree?
The requirement was to combine multiple orders (belonging to a single ship-to party) into a single delivery note. ALV tree seemed to be the perfect option for the hierarchy with the Ship-to Party being the Parent node and the orders with the same Ship-to Party to be the children nodes.
This post is in continuation to my previous post. If you wish to see a simple ALV tree report, you can refer to my earlier post. In this post, I have tried to show how some small code changes enhances the ALV tree report and makes it more user friendly.
Below are some enhancements I made:
◉ Expand/ Collapse, Select/ Unselect all nodes.
◉ Navigate to created delivery/ show error log in case no delivery is created
◉ Have a reset button in case of some error
1. Option to Expand/ Collapse all nodes and to Select/ Unselect all nodes
Created a custom GUI status (ZSTATUS) with the following buttons
Create the instance of ALV tree (go_tree). The table (gt_final1) needs to be empty at this point.
DATA: gt_final1 TYPE TABLE OF ty_final,
go_tree TYPE REF TO cl_salv_tree.
* Create ALV tree
TRY.
cl_salv_tree=>factory(
IMPORTING
r_salv_tree = go_tree
CHANGING
t_table = gt_final1 ).
CATCH cx_salv_error.
LEAVE LIST-PROCESSING.
ENDTRY.
* Set GUI STATUS
go_tree->set_screen_status( pfstatus = 'ZSTATUS'
report = sy-repid
set_functions = go_tree->c_functions_all ).
The Expand and Collapse buttons trigger the event AFTER_SALV_FUNCTION.
The Select and Unselect buttons trigger the event ADDED_FUNCTION
We have created two methods for them (along with our previously created ON_LINK_CLICK method)
METHODS: on_link_click FOR EVENT link_click
OF cl_salv_events_tree
IMPORTING columnname
node_key,
on_user_command FOR EVENT added_function
OF cl_salv_events
IMPORTING e_salv_function,
reshape_tree FOR EVENT after_salv_function
OF cl_salv_events
IMPORTING e_salv_function.
We now need to register these events
DATA: lo_events TYPE REF TO cl_salv_events_tree.
lo_events = go_tree->get_event( ).
SET HANDLER go_create->on_link_click FOR lo_events.
SET HANDLER go_create->on_user_command FOR lo_events.
SET HANDLER go_create->reshape_tree FOR lo_events.
When user clicks on SELECT / DESELECT all buttons, the method ON_USER_COMMAND is triggered. The idea is to loop at all nodes and find the one that are checkboxes and then select/ unselect them. You can find that the value for checkbox is 3 in the Attributes of class CL_SALV_ITEM
METHOD on_user_command.
*---------------------------------------------------------
* Select/ Deselect all checkboxes
*---------------------------------------------------------
DATA: lo_nodes TYPE REF TO cl_salv_nodes,
lt_nodes TYPE salv_t_nodes,
lo_item TYPE REF TO cl_salv_item,
lv_set TYPE xfeld,
lv_value TYPE salv_de_constant.
FIELD-SYMBOLS: <ls_nodes> TYPE salv_s_nodes.
* Get reference of the nodes of the tree
lo_nodes = go_tree->get_nodes( ).
* Get all the nodes of the tree
TRY.
lt_nodes = lo_nodes->get_all_nodes( ).
CATCH cx_salv_msg.
EXIT.
ENDTRY.
CASE sy-ucomm.
WHEN '&ALL'.
lv_set = abap_true. "check
WHEN '&NONE'.
lv_set = abap_false. "uncheck
ENDCASE.
* Loop at the nodes where the type is a checkbox (3)
LOOP AT lt_nodes ASSIGNING <ls_nodes>.
lo_item = <ls_nodes>-node->get_hierarchy_item( ).
lv_value = lo_item->get_type( ).
IF lo_item->is_enabled( ) = abap_true AND lv_value = 3. "Checkbox
lo_item->set_checked( lv_set ).
ENDIF.
ENDLOOP.
ENDMETHOD. "on_user_command
When the user clicks on EXPAND/ COLLAPSE buttons, the method RESHAPE_TREE gets triggered.
METHOD reshape_tree.
*---------------------------------------------------------
* Expand/ Collapse all nodes
*---------------------------------------------------------
DATA: lo_nodes TYPE REF TO cl_salv_nodes,
lo_columns TYPE REF TO cl_salv_columns.
* Get reference of the nodes of the tree
lo_nodes = go_tree->get_nodes( ).
CASE sy-ucomm.
WHEN '&EXPAND'.
lo_nodes->expand_all( ).
WHEN '&COLLAPSE'.
lo_nodes->collapse_all( ).
ENDCASE.
ENDMETHOD. "reshape_tree
This is how the Order Combination screen looks
NOTE: to know how to add heading and how the nodes were added to give a hierarchical display, you can refer to step 2 and step 4 from
this post.
Once the user clicks on Collapse button, we can see the value 0,000 for the column Order Qty as below (because Order quantity is of type QUAN and the initial value is 0,000)
To solve this, we can add one more field of a character type to our final structure and pass the value from the Order Qty actual field to the character field using a WRITE statement.
Next we need to hide the actual Order Qty column, rename the new character column as ‘Order Qty’ and align the values in it to the right. For this we can make use of following methods of class CL_SALV_COLUMNS:
◉ SET_VISIBLE
◉ SET_TECHNICAL
◉ SET_MEDIUM_TEXT
◉ SET_LONG_TEXT
◉ SET_ALIGNMENT
2. Navigate to the created delivery and display error log if the delivery is not created
In order to create the delivery, we are using BAPI_OUTB_DELIVERY_CREATE_SLS
Issues and work-around for error log display
◉ The return table from BAPI
The return table that the above-mentioned BAPI sends back to the custom report, has a bunch of messages (It can have the error message plus the success message of the delivery created). However, it is not possible to know just reading the table, that which message corresponds to which order.
This is how the return table looks in debug.
In order to display the error log we need to know the Error message and the corresponding order. To solve this, we implemented a BAdI and used the Extension out structure of the BAPI.
◉ Debugging the BAPI
A bit of debug inside the BAPI and it was clear that FM SHP_DELIVERY_CREATE_FROM_SLS was being called to create the delivery and this FM exported two tables:
LT_ITEMS_OUT — list of orders for which deliveries created
LT_VBFS — list of orders for which delivery is not created and the error message class and number
◉ Implementing BAdI and using Extension Out
We implemented the BAdI and filled Extension out structure from LT_VBFS
Code inside the BAdI:
METHOD if_dlv_create_extout~additional_output.
DATA: lt_callstack TYPE abap_callstack.
FIELD-SYMBOLS: <ls_extension_out> TYPE bapiparex,
<ls_messages> TYPE vbfs.
* Read the stack
CALL FUNCTION 'SYSTEM_CALLSTACK'
IMPORTING
callstack = lt_callstack.
SORT lt_callstack BY mainprogram.
* Continue if it is Order Combination report
READ TABLE lt_callstack TRANSPORTING NO FIELDS
WITH KEY mainprogram = 'ZORDER_COMBINATION' "our report name
BINARY SEARCH.
IF sy-subrc EQ 0.
* Fill the Extension Out with VBFS table
LOOP AT it_messages ASSIGNING <ls_messages>.
APPEND INITIAL LINE TO ct_extension_out ASSIGNING <ls_extension_out>.
<ls_extension_out>-structure = 'VBFS'.
<ls_extension_out>-valuepart1 = <ls_messages>.
<ls_extension_out>-valuepart2 = <ls_messages>+240(20).
ENDLOOP.
ENDIF.
ENDMETHOD.
◉ In the report, we now have the error and the order no. using the Extension out structure
Delivery Creation method (Only a few changes made in the method in the previous post)
If delivery is created for an order, we set the node to green color and display the delivery. On clicking the delivery, we navigate to the delivery.
If no delivery is created, we set the node to red color and give a button to show error log.
METHOD create_delivery.
*---------------------------------------------------------
* Create the delivery from the selected Orders
*---------------------------------------------------------
DATA: lt_nodes TYPE salv_t_nodes,
lo_item TYPE REF TO cl_salv_item,
lo_data TYPE REF TO data,
lv_delivery TYPE vbeln_vl,
lv_number TYPE vbnum,
lt_sales_orders TYPE STANDARD TABLE OF bapidlvreftosalesorder,
lt_created_items TYPE STANDARD TABLE OF bapidlvitemcreated,
lt_return TYPE STANDARD TABLE OF bapiret2,
lt_extension_out TYPE STANDARD TABLE OF bapiparex,
lo_item_delivery TYPE REF TO cl_salv_item,
lt_vbfs TYPE STANDARD TABLE OF vbfs.
FIELD-SYMBOLS: <ls_nodes> TYPE salv_s_nodes,
<ls_data> TYPE ty_final,
<ls_orders> TYPE bapidlvreftosalesorder,
<ls_items> TYPE bapidlvitemcreated,
<ls_vbfs> TYPE vbfs,
<ls_ext_out> TYPE bapiparex.
* Get the entire subtree
TRY.
lt_nodes = io_node->get_subtree( ).
CATCH cx_salv_msg.
EXIT.
ENDTRY.
* Here we will loop at all the nodes, find the ones that are selected
* and pass those orders to BAPI for Delivery Creation
LOOP AT lt_nodes ASSIGNING <ls_nodes>.
* Get the item details
lo_item = <ls_nodes>-node->get_hierarchy_item( ).
* Get the node data if it is checked
IF lo_item->is_enabled( ) = abap_true
AND lo_item->is_checked( ) = abap_true.
lo_data = <ls_nodes>-node->get_data_row( ).
ASSIGN lo_data->* TO <ls_data>.
IF sy-subrc EQ 0.
* Get the sales order and item
APPEND INITIAL LINE TO lt_sales_orders ASSIGNING <ls_orders>.
<ls_orders>-ref_doc = <ls_data>-vbeln.
<ls_orders>-ref_item = <ls_data>-posnr.
ENDIF.
ENDIF.
ENDLOOP.
*Call the BAPI for delivery creation
CALL FUNCTION 'BAPI_OUTB_DELIVERY_CREATE_SLS'
IMPORTING
delivery = lv_delivery
num_deliveries = lv_number
TABLES
sales_order_items = lt_sales_orders
created_items = lt_created_items
extension_out = lt_extension_out
return = lt_return.
* FIll VBFS from Extension out table
LOOP AT lt_extension_out ASSIGNING <ls_ext_out>.
APPEND INITIAL LINE TO lt_vbfs ASSIGNING <ls_vbfs>.
<ls_vbfs>+0(240) = <ls_ext_out>-valuepart1.
<ls_vbfs>+240(20) = <ls_ext_out>-valuepart2.
ENDLOOP.
SORT lt_vbfs BY vbeln.
SORT lt_created_items BY ref_doc ref_item.
IF lv_delivery IS NOT INITIAL.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'.
ELSE.
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
ENDIF.
* Next, we loop at the nodes and display the delivery
LOOP AT lt_nodes ASSIGNING <ls_nodes>.
lo_item = <ls_nodes>-node->get_hierarchy_item( ).
TRY.
lo_item_delivery = <ls_nodes>-node->get_item( 'DELIVERY' ).
CATCH cx_salv_msg.
EXIT.
ENDTRY.
IF lo_item->is_checked( ) = abap_true.
lo_item->set_checked( abap_false ).
lo_data = <ls_nodes>-node->get_data_row( ).
ASSIGN lo_data->* TO <ls_data>.
IF sy-subrc EQ 0.
* Set the delivery
READ TABLE lt_created_items ASSIGNING <ls_items>
WITH KEY ref_doc = <ls_data>-vbeln
ref_item = <ls_data>-posnr
BINARY SEARCH.
IF sy-subrc EQ 0.
<ls_data>-delivery = <ls_items>-deliv_numb.
<ls_nodes>-node->set_data_row( <ls_data> ).
ELSE.
READ TABLE lt_vbfs ASSIGNING <ls_vbfs>
WITH KEY vbeln = <ls_data>-vbeln
BINARY SEARCH.
IF sy-subrc EQ 0.
MESSAGE ID <ls_vbfs>-msgid TYPE <ls_vbfs>-msgty
NUMBER <ls_vbfs>-msgno
WITH <ls_vbfs>-msgv1 <ls_vbfs>-msgv2
<ls_vbfs>-msgv3 <ls_vbfs>-msgv4
INTO <ls_data>-logs.
ENDIF.
ENDIF.
ENDIF.
* Disable the checkbox if the delivery is created,
IF <ls_data>-delivery IS NOT INITIAL.
lo_item->set_enabled( abap_false ).
lo_item->set_icon( '@0V\QOK@' ). "OK
* Set link-click for the delivery > Now you can navigate to delivery
lo_item_delivery->set_type( if_salv_c_item_type=>link ).
* Set the node to green color
<ls_nodes>-node->set_row_style( if_salv_c_tree_style=>emphasized_positive ).
ELSE.
* Set button for error log
lo_item_delivery->set_type( if_salv_c_item_type=>button ).
lo_item_delivery->set_value( 'Display Error Log' ).
* Set the node to red color
<ls_nodes>-node->set_row_style( if_salv_c_tree_style=>emphasized_negative ).
ENDIF.
ENDIF.
ENDLOOP.
ENDMETHOD. "create_delivery
For the method ON_LINK_CLICK, we need to now check what the user has clicked on Delivery Creation button, Error Log button or the Delivery itself.
METHOD on_link_click.
*---------------------------------------------------------
* Method when the user clicks on Link or button
*---------------------------------------------------------
DATA: lo_nodes TYPE REF TO cl_salv_nodes,
lo_node TYPE REF TO cl_salv_node,
lo_data TYPE REF TO data.
FIELD-SYMBOLS: <ls_data> TYPE ty_final.
lo_nodes = go_tree->get_nodes( ).
* Get the parent node
TRY.
lo_node = lo_nodes->get_node( node_key ).
CATCH cx_salv_msg.
EXIT.
ENDTRY.
lo_data = lo_node->get_data_row( ).
ASSIGN lo_data->* TO <ls_data>.
IF sy-subrc EQ 0.
* Check what the user has clicked
CASE <ls_data>-delivery.
WHEN 'Delivery Creation'.
go_create->create_delivery( lo_node ).
WHEN 'Display Error Log'.
MESSAGE <ls_data>-logs TYPE 'I'.
WHEN OTHERS.
* Open the delivery in VL03N
SET PARAMETER ID 'VL' FIELD <ls_data>-delivery.
CALL FUNCTION 'ABAP4_CALL_TRANSACTION'
EXPORTING
tcode = 'VL03N'
skip_screen = abap_true
EXCEPTIONS
call_transaction_denied = 1
tcode_invalid = 2
OTHERS = 3.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
EXIT.
ENDCASE.
ENDIF.
ENDMETHOD. "on_link_click
Working example of the report:
There is some issue with Item 30 of Order 1000000002, so it does not get delivered
Step 1: User clicks on Delivery Creation button
Step 2: Output. If you click on Delivery 2000000001, you’ll navigate to the delivery note (VL03N)
Step 3: Click on Display Error Log
3. Ability to reset in case of some error, and resolve the error inside the order
Along with the changes done above, I tried to make this report a bit more user-friendly. Below is the summary of the changes (without any code snippets- I leave it as an exercise for the reader).
◉ Instead of Delivery Creation, the button is called Simulation. When this button is clicked, its name changes to Delivery Creation. The BAPI creates the delivery but we don’t do a COMMIT WORK and don’t display the Delivery in the output.
◉ In case of an error, we still show the error log. We also show a Reset button at Ship-to node in the Sales Order Column.
◉ In case of no error, there would be no Reset button
The user now has 2 options :-
1. Click on Delivery Creation button to continue with Delivery Creation of the possible orders. (In this case, all yellow nodes become green nodes and the delivery number gets displayed.)
2. Click on Reset button to do a ROLLBACK and reset everything. For the order with error, the user can now also navigate to the order, remove the error and try with the Simulation again.
The purpose of this post was to show that just with a few lines of code, the report output seems much nicer than the previous version.
No comments:
Post a Comment