Change the data for Export Controls checks

Overview

You can adapt the data used in the Export Controls check. Just implement the BAdI for each business object accordingly:

Data objectBAdI
Sales document/AEB/CMP_EC_ORDER_06
Delivery/AEB/CMP_EC_DLV_03
Purchase document/AEB/CMP_EC_PD_03
Service order (ERP)/AEB/CMP_EC_SO_03
Service transactions (S/4HANA)/AE1/CMP_EC_ST_02

Data structure

Before you start the implementation, take a look on the data structures used in the Export Controls check:

The whole transaction is provided in an object-oriented structure. To access the relations and fields, use the given get/set/add -methods.
A transaction is the checked document, i.e. a sales order. Linked to that are the according items i.e. sales order items. For Trade Compliance Management all checks are executed on item level, which also hold the classification values such as the Export Control Number

Concept explanation: How to change the item quantity

Let's start with a simple implementation to change the quantity on item level:

DATA:
    items         TYPE /aeb/if_cmp_pb_ec_item_do=>tt_ec_item_do,
    curr_item     TYPE REF TO /aeb/if_cmp_pb_ec_item_do,
    quantity type TYPE REF TO /AEB/CL_01_DEC_15_3_NV.
    
items = im_value->get_items( ).
loop at items into curr_item.
	quantity = im_nullable_value_factory->dec_15_3( '1' ).
	curr_item->set_quantity( quantity ).
endloop.

Explanation of this code snippet:

  • First, we access the items by calling the method get_items. The result ist a list of instances of type interface /aeb/if_cmp_ec_item_do.
  • Then we loop over these instances. The quantity and any other plain field is provided as a so called "nullable value". We use the nullable values to differ between "null" and "0.0".
  • To create such nullable value use the factory parameter _imnullable_value_factory. This parameter has methods to create each type that is supported.
  • In our example we use the type dec15_3. After you have assigned the quantity value to the according variable, you can apply it to the item by using the set-method _set_quantity.

Using the context instead of reading the database

Ok now let us extend the example by using a z-field of table VBAP to set the quantity:

DATA:
    items         TYPE /aeb/if_cmp_pb_ec_item_do=>tt_ec_item_do,
    curr_item     TYPE REF TO /aeb/if_cmp_pb_ec_item_do,
    quantity type TYPE REF TO /AEB/CL_01_DEC_15_3_NV,
   	item_ref TYPE REF TO /AEB/CL_01_CHAR_255_NV,
    posnr type posnr,
    vbaps type /AEB/CMP_PB_VBAPS,
    curr_vbap type vbap.
    
items = im_value->get_items( ).
loop at items into curr_item.
	item_ref = curr_item->get_reference_id( ).
  posnr = item_ref->v.
  vbaps = im_Context_bc->get_vbaps( ).
  read table vbaps into curr_vbap with key posnr = posnr.
  
	quantity = im_nullable_value_factory->dec_15_3( curr_vbap-z_quantity ).
	curr_item->set_quantity( quantity ).
endloop.

As you can see, we don't read the vbap from the database, and rather get it from the context object using the method get_vbaps. To find the right item, we use the reference id from the curr_item.

Changing classifications

This example shows how to overwrite the export control number for the EU jurisdiction:

DATA:
    items         TYPE /aeb/if_cmp_pb_ec_item_do=>tt_ec_item_do,
    curr_item     TYPE REF TO /aeb/if_cmp_pb_ec_item_do,
   	item_ref      TYPE REF TO /AEB/CL_01_CHAR_255_NV,
    classification   TYPE REF TO /aeb/if_cmp_pb_ec_prd_cls_do,
    classifications TYPE /AEB/IF_CMP_PB_EC_PRD_CLS_DO=>TT_EC_PRODUCT_CLASSIFICATION,
    classification_code TYPE REF TO /AEB/CL_01_CHAR_50_NV,
    classification_number TYPE REF TO /AEB/CL_01_CHAR_50_NV,
    posnr type posnr,
    vbaps type /AEB/CMP_PB_VBAPS,
    curr_vbap type vbap.
    
items = im_value->get_items( ).
loop at items into curr_item.
	item_ref = curr_item->get_reference_id( ).
  posnr = item_ref->v.
  vbaps = im_Context_bc->get_vbaps( ).
  read table vbaps into curr_vbap with key posnr = posnr.
  
  classifications = curr_item->get_product_classifications( ).
  loop at classifications into classification.
  	classification_code = classification->GET_CLASSIFICATION_IDENTCODE( ).
    if classification_code->v = 'ClassificationAusfuhrliste'.
    	classification_number = im_nullable_value_factory->char_50( curr_vbap-z_ecn ).
      classification->set_classification_number( classification_number ).
    endif.
  endloop.	
endloop.

This example shwos that one just needs to identify the according classification and then update it. But it's not neccessary to update the item or the transaction.

Adding classifications

You can as well add classifications. The following example does that:

DATA:
  items               TYPE /aeb/if_cmp_pb_ec_item_do=>tt_ec_item_do,
  curr_item           TYPE REF TO /aeb/if_cmp_pb_ec_item_do,
  item_ref            TYPE REF TO /aeb/cl_01_char_255_nv,
  classification      TYPE REF TO /aeb/if_cmp_pb_ec_prd_cls_do,
  classifications     TYPE /aeb/if_cmp_pb_ec_prd_cls_do=>tt_ec_product_classification,
  classification_code TYPE REF TO /aeb/cl_01_char_50_nv,
  classification_no   TYPE REF TO /aeb/cl_01_char_50_nv,
  posnr               TYPE posnr,
  vbaps               TYPE /aeb/cmp_pb_vbaps,
  curr_vbap           TYPE vbap.
    
items = im_value->get_items( ).
loop at items into curr_item.
  item_ref = curr_item->get_reference_id( ).
  posnr = item_ref->v.
  vbaps = im_Context_bc->get_vbaps( ).
  read table vbaps into curr_vbap with key posnr = posnr.  
  classification = im_data_object_factory->new_cmp_pb_ec_prd_cls_do( ).
  classification_code = im_nullable_value_factory->char_50( 'ADD_CODE_1' ).
  classification->set_classification_identcode( classification_code ).
  classification_no = im_nullable_value_factory->char_50( curr_vbap-add_code_1_val ).
  classification->set_classification_number( classification_no ).  
  curr_item->add_product_classification( classification ).
endloop.

Use the "add"-method to add a new classification. To create an instance for a new classificatio,n use the method "new_cmp_pb_ec_prd_cls_do" of the provided parameter "im_data_object_factory".

Get the values based on profiles maintained in Product Classification

And now we like to use Product Classification to overwrite the classifications by profile to the items.

DATA:
  items               TYPE /aeb/if_cmp_pb_ec_item_do=>tt_ec_item_do,
  curr_item           TYPE REF TO /aeb/if_cmp_pb_ec_item_do,
  classifications     TYPE /aeb/if_cmp_pb_ec_prd_cls_do=>tt_ec_product_classification.
    
items = im_value->get_items( ).
loop at items into curr_item.
   classifications = im_classification_bc->get_classifications_for( 
   		im_classification_profile = 'CUS_CLS_PROFILE' "this profile has exist in Product Classification
        im_ec_item = item ).
                
   curr_item->set_product_classifications( classifications ).   
endloop.

Read classifications with date today

Sometimes

DATA:
    classifications        TYPE /aeb/if_cmp_pb_ec_prd_cls_do=>tt_ec_product_classification,
    standard_decisive_date TYPE REF TO /aeb/cl_01_dats_nv.
  
  LOOP AT im_value->get_items( ) INTO item.
  *save the original decisive date first so it can be restored later for the item
  standard_decisive_date = item->get_decisive_date( ).
  *always use date today to read classifications 
    item->set_decisive_date( im_nullable_value_factory->dats( sy-datum ) ).
    classifications = im_classification_bc->get_classifications_for(
        im_ec_item                = item
        im_classification_profile = 'Choose the right profile code here'  ). 
    item->set_product_classifications( classifications ).
   *restore the decisive date 
    item->set_decisive_date( standard_decisive_date ).
  ENDLOOP.

Manage item values

If you have the requirement to overwrite the values you must keep in mind that whenever you change the document value you also should update the plugin values (also called jurisdiction values), which simply represent the same value converted to to amounts in different currencies. (These additional amounts per currency are relevant, for example, if licenses are setup in a currency different to the document currency. In general, the document currency combined with the plug-in values results in a list of all currencies that are transferred to Trade Compliance Management. Each currency may only appear once here. This list is transferred in the EC check as valueOfGoods. When a license is created, the document currency is transferred as the original value and the appropriate currency for the jurisdiction of the license is determined and also transferred. All possibly required currencies for the creation of the licenses must therefore be available.)

So let's update the document values and also the additional jurisdiction value for EU-jurisdiction (here we need the value in EUR currency). For our next case the value we like to use as document value is again stored in a z-field of table vbap.

DATA:
      items            TYPE /aeb/if_cmp_pb_ec_item_do=>tt_ec_item_do,
      curr_item        TYPE REF TO /aeb/if_cmp_pb_ec_item_do,
      item_ref         TYPE REF TO /aeb/cl_01_char_255_nv,
      posnr            TYPE posnr,
      vbaps            TYPE /aeb/cmp_pb_vbaps,
      curr_vbap        TYPE vbap,
      amount_of_money  TYPE REF TO /aeb/if_cmp_pb_ec_aom_do,
      value            TYPE REF TO /aeb/cl_01_dec_15_2_nv,
      amount_of_moneys TYPE /aeb/if_cmp_pb_ec_aom_do=>tt_ec_amount_of_money.

    items = im_value->get_items( ).
    LOOP AT items INTO curr_item.
      item_ref = curr_item->get_reference_id( ).
      posnr = item_ref->v.
      vbaps = im_context_bc->get_vbaps( ).
      READ TABLE vbaps INTO curr_vbap WITH KEY posnr = posnr.

      amount_of_money = curr_item->get_document_value( ).
      value = im_nullable_value_factory->dec_15_2( curr_vbap-z_doc_value ).
      amount_of_money->set_value( value ).

      amount_of_moneys = curr_item->get_plugin_values( ).
      LOOP AT amount_of_moneys INTO amount_of_money.
        IF amount_of_money->get_currency_iso( )->v = 'EUR'.
          value = im_nullable_value_factory->dec_15_2( curr_vbap-z_doc_value_in_eur ).
          amount_of_money->set_value( value ).
        ENDIF.
      ENDLOOP.
    ENDLOOP.

Make sure that you do not include amounts in the same currency twice in document value and in plugin value: The document currency must not be included in the plug-in currency. Also, if you add a plugin value the added currency must not already exist.

Add or change partner information

Another usecase might be that you like to add an addtional partner, because you have this partner information in Z-Fields of the VBAK table,e.g. the end user. In this example we assume that the standard won't fill the end user.

DATA:
      partner  TYPE REF TO /aeb/if_cmp_pb_ec_partner_do,
      prt_code TYPE REF TO /aeb/cl_01_char_20_nv,
      name     TYPE REF TO /aeb/cl_01_char_40_nv,
      country  TYPE REF TO /aeb/cl_01_char_2_nv,
      vbak     TYPE vbak.
    
    vbak = im_context_bc->get_vbak( ).
    
    partner = im_data_object_factory->new_cmp_pb_ec_partner_do( ).
    
    prt_code = im_nullable_value_factory->char_20( 'ENDUSER_STD' ).
    name = im_nullable_value_factory->char_40( vbak-z_euser_name ).
    ctry = im_nullable_value_factory->char_2( vbak-z_euser_ctry ).
    
    partner->set_identcode( prt_code ).
    partner->set_name1( name ).
    partner->set_country_iso( country ).
    
    im_value->add_partner( partner ).
DATA:
  curr_partner  TYPE REF TO /aeb/if_cmp_pb_ec_partner_do,
  curr_role     TYPE REF TO /aeb/cl_01_char_50_nv,
  new_identcode TYPE REF TO /aeb/cl_01_char_20_nv.

LOOP AT im_value->get_partners( ) INTO curr_partner.
  CASE curr_partner->get_role_identcode( )->v.
    WHEN 'CONSIGNOR_STD'.
      new_identcode = im_nullable_value_factory->char_20( 'Company no.' ).
      curr_partner->set_identcode( new_identcode ).
    WHEN 'SELLER_STD'.
      new_identcode = im_nullable_value_factory->char_20( 'Company no.' ).
      curr_partner->set_identcode( new_identcode ).
  ENDCASE.
ENDLOOP.

Again we use the imcontext_bc to get data for the business object. Do not use database statemens for transactional data here. For the country you have to provide the country ISO code, not the country key.
With the _add_partner
method you can easily add the partner to the transaction. In the same way it would also work for the items.

Overwrite the completion status

Now let us overwrite the completion status of the EU-ExportControls No and the ECCN from item data.

DATA:
      items             TYPE /aeb/if_cmp_pb_ec_item_do=>tt_ec_item_do,
      curr_item         TYPE REF TO /aeb/if_cmp_pb_ec_item_do,
      item_ref          TYPE REF TO /aeb/cl_01_char_255_nv,
      compl_statuss     TYPE /aeb/if_cmp_pb_ec_pcst_do=>tt_ec_product_completion_sta,
      posnr             TYPE posnr,
      vbaps             TYPE /aeb/cmp_pb_vbaps,
      curr_vbap         TYPE vbap,
      curr_compl_status TYPE REF TO /aeb/if_cmp_pb_ec_pcst_do,
      is_complete       TYPE REF TO /aeb/cl_01_boolean_nv.
    items = im_value->get_items( ).
    LOOP AT items INTO curr_item.
      item_ref = curr_item->get_reference_id( ).
      posnr = item_ref->v.
      vbaps = im_context_bc->get_vbaps( ).
      READ TABLE vbaps INTO curr_vbap WITH KEY posnr = posnr.

      compl_statuss = curr_item->get_product_completion_status( ).
      LOOP AT compl_statuss INTO curr_compl_status.
        CASE curr_compl_status->get_plugin_identcode( )->v.
          WHEN 'DE-EU-Plugin'.
            IF curr_vbap-z_eu_sta = 'X'.
              is_complete = im_nullable_value_factory->boolean( 'X' ).
            ELSE.
              is_complete = im_nullable_value_factory->boolean( '-' ).
            ENDIF.
            curr_compl_status->set_is_product_complete( is_complete ). 
          WHEN 'US-EAR-Plugin'.
            IF curr_vbap-z_us_sta = 'X'.
              is_complete = im_nullable_value_factory->boolean( 'X' ).
            ELSE.
              is_complete = im_nullable_value_factory->boolean( '-' ).
            ENDIF.
            curr_compl_status->set_is_product_complete( is_complete ).
        ENDCASE.
      ENDLOOP.
    ENDLOOP.

Exclude item from check

LOOP AT im_value->get_items( ) INTO item.
IF z_custom_logic_item_is_text_item( item ) = 'X'. "implement suitable logic here  
   item_ref_id = item->get_reference_id( ).  
   im_value->delete_item_for( item_ref_id ).  
ENDIF.  
ENDLOOP


What's Next

How to trigger questionnaires for a risk assessment process: