Change data of Export Controls check

Overview

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

For sales documents, use BAdI /AEB/CMP_EC_ORDER_06.
For deliveries, use BAdI /AEB/CMP_EC_DLV_03.
For purchase documents, use BAdI /AEB/CMP_EC_PD_03.
For service orders, use BAdI /AEB/CMP_EC_SO_03.

For service transactions (S4), use BAdI /AE1/CMP_EC_ST_02.

Before you start the implementation, have a closer 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

Simple Example on 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/ifcmp_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 give factory parameter _im_nullable_value_factory
. This parameter has a lot of methods to create each type we support.
In our case 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 classifcations

Ok, now let's 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.

In this example you see that you just have to find the classification and update it. But it is not neccessary to update the item or the transaction.

You could also add another classification. The following example will do 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.

Just use the "add"-method to add a new classification. To create an instance for a new classification use the method "new_cmp_pb_ec_prd_cls_do" of the parameter "im_data_object_factory".

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.

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.

Overwride 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.

Remove 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: