Monday 21 December 2020

ALV Tree Simple Report (Multiple orders => Single Delivery)

Requirement

We had an interesting requirement to combine multiple orders (belonging to a single ship-to party) into a single delivery note. This can be achieved using standard transaction ‘VL10’ but the custom requirement was to only be able to combine the orders that had a similar payment type. Payment type is a custom field in the Sales Order (in our case VBAK-ZPAYM_TYPE) which can be either Cash, Credit Card, Debit Card or so on.

In this blog post, I’ll show how I used ALV tree for displaying orders belonging to Ship-to in a hierarchical form.

Why ALV tree?

To display the orders which could be combined/ bundled together 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.

While developing this report I referred to SAP Standard reports “SALV_DEMO_TREE* “which are of great help.

Selection Screen

SAP ABAP Tutorial and Material, SAP ABAP Certification, SAP ABAP Exam Prep, SAP ABAP Learning

All the data that is needed is fetched. We have a type ‘ty_final’ which has Ship-to Party, Ship-to Name, Sales order, time, Material, Qty, Unit, Payment type, Delivery and others. In order to combine multiple orders into a single Delivery note, the Order Combination flag (VBKD-KZAZU) of the Sales Order must be set. Hence, we select only those orders.

This is how the Order Combination screen looks like

SAP ABAP Tutorial and Material, SAP ABAP Certification, SAP ABAP Exam Prep, SAP ABAP Learning

Creation of tree structure:

Step 1. Create an instance of ALV tree (go_tree). The table needs to be empty at this point (gt_final1 is empty)

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.

Step 2. Set the heading for the report

DATA: lo_settings TYPE REF TO cl_salv_tree_settings. 

    lo_settings = go_tree->get_tree_settings( ).

    lo_settings->set_hierarchy_header( ‘Ship-to’ ).
    lo_settings->set_hierarchy_tooltip( ‘Ship-to’ ).

    lo_settings->set_header( ‘Order Combination Report’ ).

Step 3. Register LINK_CLICK event

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.

Step 4. The building blocks of an ALV tree are called nodes.

The Ship-to Party is a parent node and the Orders are the children nodes.

Supply the data and build the hierarchy

    LOOP AT gt_final ASSIGNING <ls_final>.

* Check if there is a change in Ship-to or Payment type
* in which case a new folder needs to be created
      IF <ls_final>-kunwe NE ls_prev-kunwe
      OR <ls_final>-zpaym_type NE ls_prev-zpaym_type.

* Get the Ship-to text
        CLEAR lv_text.
        CONCATENATE ‘Ship to’
                    <ls_final>-kunwe
                    <ls_final>-name1
          INTO lv_text SEPARATED BY space.
* Here we get the key to the Ship-to node, we will use it for Orders 
* belonging to this Ship-to
* Create new folder for Ship-to
        lv_kunwe_key = go_create->add_tree_node( iv_text = lv_text ).      
      ENDIF.

* Keep changing the flag to get alternate background color
* Changing colors as Order no. changes
      IF ls_prev-vbeln NE <ls_final>-vbeln.
        IF lv_set_style IS INITIAL.
          lv_set_style = abap_true.
        ELSE.
          lv_set_style = abap_false.
        ENDIF.
      ENDIF.

* Add new item in the folder
* Add the node with the relevant data
* Here we give the key to the Ship-to node, this way the Order 
* becomes the child of the Ship-to node
      go_create->add_tree_node( iv_nkey      = lv_kunwe_key
                                is_data      = <ls_final>
                                iv_set_type  = if_salv_c_item_type=>checkbox
                                iv_set_style = lv_set_style ).

* Store the current line as "previous" for further processing
      ls_prev = <ls_final>.

    ENDLOOP.

Below is the code along with comments for method ADD_TREE_NODE.

The idea is to distinguish between orders by changing background colors with changing Sales Orders, to set a text for Ship-to Node, to create a button at Ship-to node Delivery Column, and create checkboxes at Sales Order nodes.

 METHOD add_tree_node.
*---------------------------------------------------------
* Add a node to the ALV tree
*---------------------------------------------------------

    DATA: lo_nodes TYPE REF TO cl_salv_nodes,
          lo_node  TYPE REF TO cl_salv_node,
          lo_item  TYPE REF TO cl_salv_item,
          lo_item_delivery  TYPE REF TO cl_salv_item.

    lo_nodes = go_tree->get_nodes( ).

* We use the standard method to add the node
    TRY.
        lo_node = lo_nodes->add_node(
         related_node   = iv_nkey
         relationship   = if_salv_c_node_relation=>last_child
         data_row       = is_data
         collapsed_icon = '@5F@'    "space
         expanded_icon  = '@5F@'    "space
         text           = iv_text ).
      CATCH cx_salv_msg. 
        LEAVE LIST-PROCESSING.
    ENDTRY.

* We provided text only for Ship-to node. 
* The below code will set the text of the node (viz. Ship-to 12345 ABC Org.)
* Set the text for the node
    IF iv_text IS NOT INITIAL.
      lo_node->set_text( iv_text ).
    ENDIF.

* In order to distinguish between Orders, we set different background colors.  
* The below code sets blue color as background
    IF iv_set_style IS NOT INITIAL.
      lo_node->set_row_style( if_salv_c_tree_style=>emphasized_b ).
    ENDIF.

    lo_item = lo_node->get_hierarchy_item( ).


    IF iv_nkey IS INITIAL.
* For the Ship-to node, we take the Delivery column and set it to Button
      TRY.
          lo_item_delivery = lo_node->get_item( 'DELIVERY' ).
        CATCH cx_salv_msg. 
          EXIT.
      ENDTRY.
      lo_item_delivery->set_type( if_salv_c_item_type=>button ).
      lo_item_delivery->set_value( ‘Delivery Creation’ ).
      
    ELSE.
* For the order nodes, we get the entire row and Set it as editable checkbox
      lo_item->set_type( if_salv_c_item_type=>checkbox ).
      lo_item->set_editable( abap_true ).

* Set the data of the node
      lo_node->set_data_row( is_data ).
      lo_nodes->expand_all( ).

    ENDIF.

* Return the node key
    rv_nkey = lo_node->get_key( ).

  ENDMETHOD.                    "add_tree_node

Once the orders are selected, and the corresponding “Delivery Creation” button is clicked, it is a link_click event for which we have the below method.

SAP ABAP Tutorial and Material, SAP ABAP Certification, SAP ABAP Exam Prep, SAP ABAP Learning

METHODS: on_link_click FOR EVENT link_click
OF cl_salv_events_tree
IMPORTING columnname
node_key.

We have registered an event for this method (step 3 in create tree structure)

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_nodes = go_tree->get_nodes( ).

* Get the parent node
    TRY.
        lo_node = lo_nodes->get_node( node_key ).
      CATCH cx_salv_msg. 
        EXIT.
    ENDTRY.

    go_create->create_delivery( lo_node ).

  ENDMETHOD.                    "on_link_click
 

Delivery Creation Logic


The BAPI used for the delivery creation was BAPI_OUTB_DELIVERY_CREATE_SLS.

The method CREATE_DELIVERY is explained below.

The idea is to loop at all the nodes, and get the orders that were selected and pass them to the BAPI. Once the delivery is created, we display it in the ‘Delivery’ column corresponding to the sales orders

  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. 

    FIELD-SYMBOLS: <ls_nodes>  TYPE salv_s_nodes,
                   <ls_data>   TYPE ty_final,
                   <ls_orders> TYPE bapidlvreftosalesorder,
                   <ls_items>  TYPE bapidlvitemcreated. 

* 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 were 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 
        return            = lt_return.


    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, loop at the nodes and display the delivery 
    LOOP AT lt_nodes ASSIGNING <ls_nodes>.
      lo_item = <ls_nodes>-node->get_hierarchy_item( ).
      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> ).
          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
        ENDIF.
      ENDIF.
    ENDLOOP. 

  ENDMETHOD.                    "create_delivery

Final Output

SAP ABAP Tutorial and Material, SAP ABAP Certification, SAP ABAP Exam Prep, SAP ABAP Learning

The purpose was to show how one can create a basic report using ALV tree.

I have implemented other functionalities of ALV tree which makes this report quite user friendly. Some of these enhancements are:

1. Ability to navigate to the created Delivery
2. Display error log in case an order cannot be delivered
3. Ability to select/ unselect all the checkboxes in a single click
4. Expand/ Collapse all the nodes in a single click

And the best part is all these can be implemented with only a few lines of code, but makes the report look more appealing.

No comments:

Post a Comment