Skip to Content
Technical Articles
Author's profile photo Emanuel Grabler

Enhancing the Freight Order Overview – Part 1

Especially with the new development around Advance Shipping and Receiving released in S/4 HANA 2020 OP the Overview, as the central place for execution processing, became one of the most important screens in our Freight Order. But as we learned from Ben Parker “with great power comes great responsibility” and in our case this is the responsibility to serve and handle also customer specific requirements/enhancements.

Overview%20Example%20S/4HANA%20OP%202020

Overview Example S/4HANA OP 2020

As the overview is a completely transient UI, which is built up over different entities and not based on FBI with a direct mapping of views to BO nodes, the enhancement process is rather specific and requires some knowledge around the underlying UI framework we introduced called TBI(Transient BOBF Integration) Sidenote: at least it is since 9.4, for the long term TM supporters you might remember the old BO transient node TOR~Overview.

In this two part enhancement guide series, I will describe the steps necessary to enhance the overview by a custom column filled by a field from a persisted field as well as a custom action to change this field as per the example requirement described below. The first part will focuse on required DDIC, BO and ABAP enhancements while the second part will mainly include the corresponding enhancements in the FPM layer.

Remark: Several Steps could have been performed in a different way, e.g. using customize component on the FPM side. Feel free to comment if you want to share any best practices from your projects on how you would alternatively tackle such a requirement.

Hope you enjoy:)

 

Requirement: The customer wants to add an additional status to the WH Load/Unloading Stop Level. This Status should be nicely visible represented by a traffic light on the corresponding lines of the TOR overview. The user should be able to change the status on either a single or multiple Sub-Stop instance at once, as he wants to e.g. trigger PPF activities based on the status.

An additional NFR is to not do any enhancements or modifications to any ABAP coding.

Data Structure & BO Enhancements

In this section we will take part of the structural changes on the backend object, that will enable us to store and manipulate our customer specific status.

DDIC Enhancement

This is a well known step for most of you and mainly included for the sake of completeness.

To properly enhance the structure of an existing standard BO node, navigate via the BO to the structure assigned to the BO node. In our case, for the TOR~STOP node, this is structure /SCMTMS/S_TOR_STOP. Identify the named include EEW, this is the include intended for customer appends, which is part of every major TOR BO node. Navigate to the underlying structure of the include (/SCMTMS/INCL_EEW_TOR_STOP). Click on Append Structure and create a new structure as per the naming convention of your project. Include a field in your new structure with a data element required for your scenario. In our scenario the field MY_STATUS, will be a “boolean” and only have 2 values X and blank. Save and activate your changes.

Make sure that the activation was completely successful. Specifically check that DB table /SCMTMS/D_TORSTP is active and includes your new field. Speaking from experience, missing authorization and ending up with a partially active DB table, is a save way to project visibility. 😉

TOR%7ESTOP%20Structure%20including%20Customer%20Specific%20Status%20Field

TOR~STOP Structure including Customer Specific Status Field

BO Enhancements

Open Transaction BOBX.

On the Header Toolbar Select Business Object Processing Framework -> Create -> Business Object Enhancement

Enter the name of your new enhancement business object according to the naming conventions of your project. For Super Business Object enter /SCMTMS/TOR.

Creation%20of%20BO%20Enhancement%20Object

Creation of BO Enhancement Object

In the result tree you will find a new a new entry for your enhancement Object.

Enhancement%20Object%20in%20Business%20Object%20Browser

Enhancement Object in Business Object Browser

Navigate to your enhancement object. The Object contains the structure of its original Super BO /SCMTMS/TOR.

Click on the main entry of your BO. Enter a Constant Interface name following your naming convention. On the toolbar click: Extra -> Generate Constant Interface

(You can also use the propose repository name functionality before to name your constant interface).

In the node tree of your Enhancement Object. Navigate to Node STOP -> Actions. Right click on folder actions and select create Action.

Create Action with the name SET_MY_STATUS_X.

Set the action cardinality to Multiple Node Instances, as we want to be able to set the status for multiple stop instances via a single action call.

Repeat the steps above and create an additional action and call it SET_MY_STATUS_BLANK

In a separate window open transaction SE24 to implement the class to implement the logic for the actions which change the value of the status.

For this simple case we will use the same implementation class to handle both actions, setting the status to X or blank.

In the newly created class navigate to tab Interfaces and add the BOBF action interface /BOBF/IF_FRW_ACTION.

Navigate to the tab Methods and implement interface method /BOBF/IF_FRW_ACTION~EXECUTE. In the simple example logic below, we set the MY_STATUS field on the corresponding stop to either blank or X, depending on which action is currently called, which can be identified via the is_ctx-act_key, so we can use a single action implementation for both BO actions.

  METHOD /bobf/if_frw_action~execute.

    DATA: lt_d_tor_stop TYPE /scmtms/t_tor_stop_k,
          lt_mod        TYPE /bobf/t_frw_modification.

    "read the stop data
    io_read->retrieve(
      EXPORTING
        iv_node                 = /scmtms/if_tor_c=>sc_node-stop
        it_key                  = it_key
      IMPORTING
        et_data                 = lt_d_tor_stop ).

    "now set the indicator based on which action we are calling
    LOOP AT lt_d_tor_stop ASSIGNING FIELD-SYMBOL(<s_d_tor_stop>).
      <s_d_tor_stop>-my_status = SWITCH #( is_ctx-act_key WHEN zom_if_tor_c=>sc_action-stop-set_my_status_blank
                                                               THEN abap_false
                                                          WHEN zom_if_tor_c=>sc_action-stop-set_my_status_x
                                                               THEN abap_true ).
      "return a success message if desired
    ENDLOOP.

    /scmtms/cl_mod_helper=>mod_update_multi(
      EXPORTING
        it_data            = lt_d_tor_stop
        iv_node            = /scmtms/if_tor_c=>sc_node-stop
        iv_bo_key          = /scmtms/if_tor_c=>sc_bo_key
      CHANGING
        ct_mod             = lt_mod ).

    io_modify->do_modify( lt_mod ).

  ENDMETHOD.

Assign the implementation class to the previously created BO Actions and save your changes.

Implementation%20Class%20to%20BO%20Action%20assignment

Implementation Class to BO Action assignment

UI Feeder Class Developments

Structure enhancements

The standard overview uses structure /SCMTMS/S_UI_TBI_OVW, which defines the field catalogue(columns) for the Overview tree UIBB. In Order to enhance our own Overview UI, we must create an enhanced version.

Via SE11, create a new structure that contains the standard structure as an include and your custom fields in addition. In our case we add the status itself + an icon representation of the status. The suffix TRA is added as the Feeder Class buffer will use this suffix later for the include, we will append.

UI%20Structure%20Enhancements

UI Structure Enhancements

The buffer is constructed of multiple named includes that are mapped again to the corresponding backend instances, which are included in the corresponding UI instance + a transient include for all kind of fields which are not mapped to a certain instance.
For example take a location entry from the Overview, which contains information from multiple instances: the root, the departure stop it is representing, the arrival stop it is representing.

In our case we will keep it a bit simple and will just add the fields to the transient include, as this gives us the most flexibility to adapt to requirements, where the status should be available on other intance e.g. for items. This is for example also the case for the handling execution status, were you have a single column but it is filled from different backend nodes depending if we are on an Item or Stop entry.

Therefore we will append the same structure we added to our Stop node to structure /SCMTMS/S_TBI_BUF_UI_OVW_TRANS.

Custom Feeder Class

Now the actual interessting part can start as we get to the enhancement of the new TBI based feeder class. The standard Overview UI uses feeder class /SCMTMS/CL_UI_TBI_TOR_OVW_BO and our own feeder class will inherit from the standard. Therefore create the class as described below:

Custom%20UI%20Feeder%20Class

Custom UI Feeder Class

Navigate to tab Methods and create a redefinition for:

  • INIT: Set the new UI Structure to include additional fields/columns:
      method /SCMTMS/IF_UI_TBI_CORE~INIT.
    
        super->/scmtms/if_ui_tbi_core~init(
          EXPORTING
            it_parameter      = it_parameter
            iv_lean           = iv_lean
            io_data_interface = io_data_interface ).
    
        mv_ui_structure_name = 'ZOM_S_UI_TBI_OVW'.
        get_substructure_names( EXPORTING
                                  iv_structure_name     = mv_ui_structure_name
                                CHANGING
                                  cv_ui_root_structure  = mv_ui_structure_root
                                  cv_ui_item_structure  = mv_ui_structure_item
                                  cv_ui_sdep_structure  = mv_ui_structure_sdep
                                  cv_ui_sarr_structure  = mv_ui_structure_sarr
                                  cv_ui_stage_structure = mv_ui_structure_stage
                                  cv_ui_trans_structure = mv_ui_structure_trans ).
    
    *---- Set hierarchy type for BO overview
        mv_consumer  = /scmtms/if_tor_const=>sc_overview_consumer-overview.
        mv_current_hier_type = /scmtms/cl_tor_hierarchy_cust=>c_overview_hierarchy.
        mv_hswitch   = sc_hswitch-sameconsumer.
    
      endmethod.​

 

  • ADAPT_DEFINITION: Rename the new columns and actions, via mapping to OTR Texts. You can also do the naming completely in the UI Configuration layer. But in case you are using multiple configurations with the same feeder class it is recommended to implement this in the backend.
      METHOD /scmtms/if_ui_tbi_fpm~adapt_definition.
    
        "call superclass for standard logic
        super->/scmtms/if_ui_tbi_fpm~adapt_definition(
          EXPORTING
            is_uibb_key          = is_uibb_key
          CHANGING
            ct_field_descr_form  = ct_field_descr_form
            ct_sort_reference    = ct_sort_reference
            ct_field_descr_list  = ct_field_descr_list
            ct_field_descr_tree  = ct_field_descr_tree
            ct_action_definition = ct_action_definition
            ct_dnd_definition    = ct_dnd_definition
            ct_row_actions       = ct_row_actions
            cs_options_form      = cs_options_form
            cs_options_list      = cs_options_list
            cs_options_tree      = cs_options_tree ).
    
        "add new columns descriptions, tx is with reference to an OTR Text, chose or create a fitting OTR text as required in your project
        DATA(lt_field) = VALUE /scmtms/cl_ui_tbi_util=>tt_fdescr(
            ( f = 'MY_STATUS_ICONTRA' tx = '/SCMTMS/UI_CMN/OC_STATUS' ) ).
    
        /scmtms/cl_ui_tbi_util=>chg_fcat_t(
          EXPORTING
            it_text  = lt_field
          CHANGING
            ct_descr = ct_field_descr_tree ).
    
    
        "Action defintion + description
        DATA(lt_action) = VALUE /scmtms/cl_ui_tbi_util=>tt_adescr(
            ( id = 'SET_MY_STATUS_X' tx = '/SCMTMS/UI_CMN/SET_STATUS' )
            ( id = 'SET_MY_STATUS_BLANK' tx = '/SCMTMS/UI_CMN/RESET_STATUS' ) ).
    
        /scmtms/cl_ui_tbi_util=>chg_action(
          EXPORTING
            it_action_descr      = lt_action
          CHANGING
            ct_action_definition = ct_action_definition ).
    
    
      ENDMETHOD.​
  • _INIT_INTERNAL: Map the UI actions to a specific node. As the overview is not based on a single note but a combination of stops, items etc. the feeder needs to know to which BO instance the action call on a UI must be forwarded to. In our case we forward the UI action to the Stop action we previously added to our custom BO and the keys are pulled from the corresponding includes ARR and DEP. This is quite nice as it does not require us to build a mapping from UI instance to backend instance by hand. So even if we would introduce a Status on item level that is supposed to be shown in the same column, we could simply map for the element_cat=Item to another backend action and let the framework determine the keys from the Item group in the buffer structure.
      METHOD _init_internal.
    
        CLEAR:
          er_data,
          ev_convclass,
          es_object,
          et_action,
          et_elemcat,
          et_fieldsource,
          et_notifnode.
    
        super->_init_internal(
          EXPORTING
            it_parameter   = it_parameter
          IMPORTING
            er_data        = er_data
            ev_convclass   = ev_convclass
            es_object      = es_object
            et_action      = et_action
            et_notifnode   = et_notifnode
            et_elemcat     = et_elemcat
            et_fieldsource = et_fieldsource ).
    
        "map the UI action to the correponding action on BO node stop
        INSERT VALUE #( ui_action         = 'SET_MY_STATUS_X'
                        group             = sc_ui_groups-stop_arrival
                        element_cat       = sc_tbi_element_cat-stop_asr
                        bo_key            = /scmtms/if_tor_c=>sc_bo_key
                        bo_action         = zom_if_tor_c=>sc_action-stop-set_my_status_x )
                      INTO TABLE et_action.
    
        INSERT VALUE #( ui_action         = 'SET_MY_STATUS_X'
                        group             = sc_ui_groups-stop_departure
                        element_cat       = sc_tbi_element_cat-stop_asr
                        bo_key            = /scmtms/if_tor_c=>sc_bo_key
                        bo_action         = zom_if_tor_c=>sc_action-stop-set_my_status_x )
                      INTO TABLE et_action.
    
    
        INSERT VALUE #( ui_action         = 'SET_MY_STATUS_BLANK'
                        group             = sc_ui_groups-stop_arrival
                        element_cat       = sc_tbi_element_cat-stop_asr
                        bo_key            = /scmtms/if_tor_c=>sc_bo_key
                        bo_action         = zom_if_tor_c=>sc_action-stop-set_my_status_blank )
                      INTO TABLE et_action.
    
        INSERT VALUE #( ui_action         = 'SET_MY_STATUS_BLANK'
                        group             = sc_ui_groups-stop_departure
                        element_cat       = sc_tbi_element_cat-stop_asr
                        bo_key            = /scmtms/if_tor_c=>sc_bo_key
                        bo_action         = zom_if_tor_c=>sc_action-stop-set_my_status_blank )
                      INTO TABLE et_action.
    
      ENDMETHOD.​
  • SET_ACT_PROP: Here we determine for all UI instances if the actions should be available or not. As we want to support the logic for the ASR stop only, we filter the instances based on the element_cat = ASR.
      METHOD set_act_prop.
    
        super->set_act_prop(
          EXPORTING
            it_key       = it_key
            it_parameter = it_parameter
          CHANGING
            ct_act_prop  = ct_act_prop
            cv_changed   = cv_changed ).
    
    
        LOOP AT it_key ASSIGNING FIELD-SYMBOL(<s_key>).
          READ TABLE mt_data WITH KEY key COMPONENTS key = <s_key>-key ASSIGNING FIELD-SYMBOL(<s_data>).
          CHECK sy-subrc = 0.
    
          "Action should only be enabled for ASR sub-stops
          IF <s_data>-element_cat = sc_tbi_element_cat-stop_asr.
            DATA(lv_enable_act) = abap_true.
          ELSE.
            lv_enable_act = abap_false.
          ENDIF.
    
          "set properties for action to set status to 'X'
          READ TABLE ct_act_prop
          ASSIGNING FIELD-SYMBOL(<s_act_prop>)
          WITH TABLE KEY key_id COMPONENTS key = <s_data>-key
                                           id  = 'SET_MY_STATUS_X'.
          IF sy-subrc = 0.
            <s_act_prop>-is_enable = lv_enable_act.
          ELSE.
            INSERT VALUE #( key = <s_data>-key id = 'SET_MY_STATUS_X' is_visible = lv_enable_act is_enable = lv_enable_act ) INTO TABLE ct_act_prop.
          ENDIF.
    
          READ TABLE ct_act_prop
          ASSIGNING <s_act_prop>
          WITH TABLE KEY key_id COMPONENTS key = <s_data>-key
                                           id  = 'SET_MY_STATUS_BLANK'.
          IF sy-subrc = 0.
            <s_act_prop>-is_enable = lv_enable_act.
          ELSE.
            INSERT VALUE #( key = <s_data>-key id = 'SET_MY_STATUS_BLANK' is_visible = lv_enable_act is_enable = lv_enable_act ) INTO TABLE ct_act_prop.
          ENDIF.
    
        ENDLOOP.
    
      ENDMETHOD.​
  • BUILD_DATA_TABLE: Here we actually fill the data into the UI layer as per requirement. The build_data_table method is called inside of interface method get_data, and the nice part is that due to the structure of the method we have already all important data already buffered in corresponding tables.
  METHOD build_data_table.

    DATA: lt_data TYPE ty_t_data.
    CLEAR et_data_table.

    super->build_data_table(
      EXPORTING
        it_key                       = it_key
        it_tor_root                  = it_tor_root
        it_tor_item_all              = it_tor_item_all
        it_tor_item_relevant         = it_tor_item_relevant
        it_stop                      = it_stop
        it_stop_succ                 = it_stop_succ
        it_item_all                  = it_item_all
        it_stage_overview_d          = it_stage_overview_d
        it_exec                      = it_exec
        iv_execute_conversion        = 'X'
        iv_consumer                  = iv_consumer
        it_erp_documents             = it_erp_documents
        io_hierarchy_cust            = io_hierarchy_cust
        it_tor_driver                = it_tor_driver
        it_req_attr                  = it_req_attr
        it_ref_item                  = it_ref_item
        it_indirect_ref_item         = it_indirect_ref_item
        it_ref_stop                  = it_ref_stop
        it_indirect_ref_stop         = it_indirect_ref_stop
        it_resource_root             = it_resource_root
        it_capa_root                 = it_capa_root
        it_kl_stop_capa_root         = it_kl_stop_capa_root
        it_summary_report            = it_summary_report
        it_charges                   = it_charges
        it_packaging_overview        = it_packaging_overview
        it_stop_succ_util            = it_stop_succ_util
        it_loc_group_result          = it_loc_group_result
        it_stop_itm_loadref          = it_stop_itm_loadref
        it_load_dir_profile_overview = it_load_dir_profile_overview
        it_att_equi_profile_overview = it_att_equi_profile_overview
        it_loc_loaddir_atteqprof     = it_loc_loaddir_atteqprof
        it_loading_stop              = it_loading_stop
        it_purchase_doc              = it_purchase_doc
      IMPORTING
        et_data_table                = et_data_table ).

    lt_data = et_data_table.

    LOOP AT it_stop ASSIGNING FIELD-SYMBOL(<s_d_stop>) WHERE asr_indicator = /scmtms/if_asr_c=>sc_tor_stop_asr_ind-asr_relevant.
      READ TABLE lt_data
      ASSIGNING FIELD-SYMBOL(<s_data>)
      WITH TABLE KEY key COMPONENTS key = <s_d_stop>-key.
      IF sy-subrc = 0.
        <s_data>-my_statustra = <s_d_stop>-my_status.
        IF <s_d_stop>-my_status = abap_false.
          <s_data>-my_status_icontra = /scmtms/if_ui_cmn_c=>sc_icons-red_led.
        ELSE.
          <s_data>-my_status_icontra = /scmtms/if_ui_cmn_c=>sc_icons-green_led.
        ENDIF.
      ENDIF.

    ENDLOOP.

    et_data_table = lt_data.

  ENDMETHOD.

 

 

So much for Part 1. With these steps performed, we are ready to consume our new feeder in the UI and create the new UI artifacts, to fullfil our customer requirement, which we will do in the second part.

 

Assigned Tags

      1 Comment
      You must be Logged on to comment or reply to a post.
      Author's profile photo Emanuel Grabler
      Emanuel Grabler
      Blog Post Author

      Find the conversation on LinkedIn:

      https://www.linkedin.com/feed/update/urn:li:activity:6855969480552574976