cancel
Showing results for 
Search instead for 
Did you mean: 
Read only

S4HANA private - split FU creation for delivery note based on dynamic criteria using BADI

Wolfgang_Mayer1
Active Participant
0 Kudos
1,188

Hello,

for a dynamic split of a delivery note into more than one freight unit, not based on static criteria like "split every 20 tons" but considering the planning result of the external TMS optimization, I found BADI "/SCMTMS/FUB_CORE" which might solve this requirement, however, I didn't find any kind of documentation or sample coding on how to use it.

The goal would be like:

  • Delivery note with item having 60 tons
    • Create freight unit #1 with 25 tons, create freight order #1 for this FU
    • Create freight unit #2 with 20 tons, create freight order #2 for this FU
    • Create freight unit #3 with 15 tons, create freight order #3 for this FU

For the next delivery note having 60tons, the requirement for splitting could be 20-21-19 tons, that's the reason why splitting via FUBR doesn't work.

It's possible to manually split a FU using the Fiori app, however, the split mentioned above based on dynamic criteria should happen when creating/saving the delivery note already.

I've checked the TM extension guide as well, however, didn't find a hint.

Any ideas?

Thanks & regards

Wolfgang

View Entire Topic
pawan__kumar
Active Participant
0 Kudos
" Class implementation for BADI /SCMTMS/FUB_CORE
CLASS zcl_tms_dynamic_fu_split DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES /scmtms/if_fub_core.
  PRIVATE SECTION.
    METHODS:
      " Main method to handle dynamic splitting
      split_fu_based_on_optimization
        IMPORTING
          it_fu_root     TYPE /scmtms/t_tor_root_k
          it_fu_item     TYPE /scmtms/t_tor_item_tr_k
        CHANGING
          ct_fu_root     TYPE /scmtms/t_tor_root_k
          ct_fu_item     TYPE /scmtms/t_tor_item_tr_k
          ct_fu_stop     TYPE /scmtms/t_tor_stop_k
          ct_fu_stop_seq TYPE /scmtms/t_tor_stop_seq_k,
      
      " Get optimization data from external TMS
      get_optimization_data
        IMPORTING
          iv_delivery_number TYPE vbeln
        RETURNING
          VALUE(rt_opt_data) TYPE ztt_tms_optimization_data,
      
      " Create FU items with split quantities
      create_split_fu_items
        IMPORTING
          is_original_item   TYPE /scmtms/s_tor_item_tr_k
          it_optimization    TYPE ztt_tms_optimization_data
        CHANGING
          ct_fu_root         TYPE /scmtms/t_tor_root_k
          ct_fu_item         TYPE /scmtms/t_tor_item_tr_k
          ct_fu_stop         TYPE /scmtms/t_tor_stop_k
          ct_fu_stop_seq     TYPE /scmtms/t_tor_stop_seq_k.
ENDCLASS.

CLASS zcl_tms_dynamic_fu_split IMPLEMENTATION.
  METHOD /scmtms/if_fub_core~prepare_fub.
    " Only process if we have FU items to potentially split
    IF it_fu_item IS NOT INITIAL.
      split_fu_based_on_optimization(
        EXPORTING
          it_fu_root     = it_fu_root
          it_fu_item     = it_fu_item
        CHANGING
          ct_fu_root     = ct_fu_root
          ct_fu_item     = ct_fu_item
          ct_fu_stop     = ct_fu_stop
          ct_fu_stop_seq = ct_fu_stop_seq
      ).
    ENDIF.
  ENDMETHOD.

  METHOD split_fu_based_on_optimization.
    DATA:
      lt_optimization_data TYPE ztt_tms_optimization_data,
      lv_delivery_number   TYPE vbeln.
    
    " Group items by delivery note
    DATA(lt_items_by_delivery) = VALUE /scmtms/t_tor_item_tr_k( 
      FOR GROUPS <group> OF <item> IN it_fu_item
      GROUP BY <item>-base_btd_tco WITHOUT MEMBERS ( <item> ) ).
    
    LOOP AT lt_items_by_delivery ASSIGNING FIELD-SYMBOL(<ls_item_group>).
      " Extract delivery number from reference
      lv_delivery_number = <ls_item_group>-base_btd_id.
      
      " Get optimization data from external TMS
      lt_optimization_data = get_optimization_data( lv_delivery_number ).
      
      " Only proceed if we have optimization data
      IF lt_optimization_data IS NOT INITIAL.
        " Process each item in this delivery
        LOOP AT it_fu_item ASSIGNING FIELD-SYMBOL(<ls_original_item>)
          WHERE base_btd_id = lv_delivery_number.
          
          " Create split FU items based on optimization data
          create_split_fu_items(
            EXPORTING
              is_original_item = <ls_original_item>
              it_optimization  = lt_optimization_data
            CHANGING
              ct_fu_root       = ct_fu_root
              ct_fu_item       = ct_fu_item
              ct_fu_stop       = ct_fu_stop
              ct_fu_stop_seq   = ct_fu_stop_seq
          ).
        ENDLOOP.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

  METHOD get_optimization_data.
    " This method would interface with your external TMS
    " For demonstration, we'll use a mock implementation
    
    " In a real scenario, you would call the TMS API or read from a custom table
    " that stores the optimization results by delivery number
    
    " Mock data for testing - replace with actual TMS integration
    CASE iv_delivery_number.
      WHEN '80000001'. " First delivery note with 60 tons
        rt_opt_data = VALUE #(
          ( sequence = 1 optimal_quantity = 25 unit = 'TON' )
          ( sequence = 2 optimal_quantity = 20 unit = 'TON' )
          ( sequence = 3 optimal_quantity = 15 unit = 'TON' )
        ).
      WHEN '80000002'. " Second delivery note with 60 tons
        rt_opt_data = VALUE #(
          ( sequence = 1 optimal_quantity = 20 unit = 'TON' )
          ( sequence = 2 optimal_quantity = 21 unit = 'TON' )
          ( sequence = 3 optimal_quantity = 19 unit = 'TON' )
        ).
      WHEN OTHERS.
        " For other deliveries, return empty to use standard behavior
    ENDCASE.
  ENDMETHOD.

  METHOD create_split_fu_items.
    DATA:
      lv_fu_count        TYPE i,
      lv_total_processed TYPE /scmtms/tor_quantity,
      lv_remaining_qty   TYPE /scmtms/tor_quantity.
    
    " Calculate initial values
    lv_fu_count = lines( ct_fu_root ).
    lv_remaining_qty = is_original_item-quantity.
    
    " Create a new FU for each optimization entry
    LOOP AT it_optimization ASSIGNING FIELD-SYMBOL(<ls_optimization>).
      " Check if we've already allocated the full quantity
      IF lv_remaining_qty <= 0.
        EXIT.
      ENDIF.
      
      " Determine quantity for this FU (don't exceed remaining)
      DATA(lv_fu_quantity) = nmin( val1 = <ls_optimization>-optimal_quantity
                                   val2 = lv_remaining_qty ).
      
      " Adjust remaining quantity
      lv_remaining_qty = lv_remaining_qty - lv_fu_quantity.
      lv_fu_count = lv_fu_count + 1.
      
      " Create new FU root entry
      DATA(ls_new_fu_root) = VALUE /scmtms/s_tor_root_k(
        " Copy most attributes from original FU
        node_id     = /scmtms/cl_tor_services=>get_new_node_id( )
        tor_id      = |{ is_original_item-base_btd_id }-{ lv_fu_count }|
        tproc       = ct_fu_root[ 1 ]-tproc
        tor_cat     = /scmtms/if_tor_const=>sc_tor_category-freight_unit
        " ... copy other relevant attributes
      ).
      APPEND ls_new_fu_root TO ct_fu_root.
      
      " Create new FU item
      DATA(ls_new_fu_item) = is_original_item.
      ls_new_fu_item-node_id       = /scmtms/cl_tor_services=>get_new_node_id( ).
      ls_new_fu_item-parent_node_id = ls_new_fu_root-node_id.
      ls_new_fu_item-quantity      = lv_fu_quantity.
      ls_new_fu_item-base_quantity = lv_fu_quantity.
      " ... set other item attributes as needed
      APPEND ls_new_fu_item TO ct_fu_item.
      
      " Copy stop sequences (simplified - in reality would need more logic)
      LOOP AT ct_fu_stop_seq ASSIGNING FIELD-SYMBOL(<ls_stop_seq>)
        WHERE parent_node_id = is_original_item-parent_node_id.
        DATA(ls_new_stop_seq) = <ls_stop_seq>.
        ls_new_stop_seq-node_id = /scmtms/cl_tor_services=>get_new_node_id( ).
        ls_new_stop_seq-parent_node_id = ls_new_fu_root-node_id.
        APPEND ls_new_stop_seq TO ct_fu_stop_seq.
      ENDLOOP.
    ENDLOOP.
    
    " Remove the original FU if we created splits
    IF lines( it_optimization ) > 0 AND lv_remaining_qty = 0.
      " Remove original FU root
      DELETE ct_fu_root WHERE node_id = is_original_item-parent_node_id.
      
      " Remove original FU items
      DELETE ct_fu_item WHERE parent_node_id = is_original_item-parent_node_id.
      
      " Remove original stop sequences
      DELETE ct_fu_stop_seq WHERE parent_node_id = is_original_item-parent_node_id.
    ENDIF.
  ENDMETHOD.
ENDCLASS.
Wolfgang_Mayer1
Active Participant
0 Kudos
Hello, I doubt I can put this coding into the only available method /SCMTMS/IF_EX_FUB_CORE~FACTORY of BADI /SCMTMS/FUB_CORE, right? Parameters available there: IS_PARAMETER, IO_INPUT, IO_TRQ_DATA, CO_FUB