Wednesday, May 10, 2017

BOL programming tips

When you program in the Business Object Layer you often use class CL_CRM_BOL_CORE e.g. for starting up the BOL or retrieve a Root Object. The latter is done using method GET_ROOT_ENTITY and requires the internal GUID of the object. Method GET_ACCESS_ENTITIES however retrieves objects using business keys like shown in the BOL Model Browser. This method can handle Access Objects, like the name suggests, but also Root Objects.

Let me provide an example of both usages.

Example 1: Retrieve a Contact Person using its Partner numbers

    ls_genil_obj      type crmt_genil_obj_instance,
    lo_contact_person type ref to cl_crm_bol_entity.

  "Retreive the contact person as (root) entity using its business key
  ls_genil_obj-object_name = 'BuilContactPerson'.
  ls_genil_obj-object_id   = cl_crm_genil_container_tools=>build_object_id(
    value crmst_header_conp_obj_buil(
      bp_number   = '200492784'
      conp_number = '200492786' ) ).
  lo_contact_person = cl_crm_bol_core=>get_instance( )->get_access_entities(
    value #( ( ls_genil_obj ) ) )->get_first( ).

Example 2: Retrieve an Order Item using its GUID

  ls_genil_obj-object_name = 'BTAdminI'.
  ls_genil_obj-object_id   = cl_crm_genil_container_tools=>build_object_id(
    conv crmt_object_guid('005056001C931EE5A28CDE69D5525A71') ).
  lo_order_item = cl_crm_bol_core=>get_instance( )->get_access_entities(
    value #( ( ls_genil_obj ) ) )->get_first( ).

Note that the parent entity is not available this way:

  lo_parent = lo_order_item->get_parent( ).  "Initial!

The root entity however is available e.g. to get the sold-to party:

  lv_account_name = lo_order_item->get_properties_by_bpath( 
    `.../BTOrderHeader/BTHeaderPartnerSet/BTPartner_00000001/BTBusinessPartner/@ACCOUNT_NAME` ).

The last piece of code uses a BPath-expression to navigate from the order item to the root object BTOrder (using the "...") and from there follows several relations to finally read the Account Name. 

I am a big fan of BPath because of its elegant and compact way to navigate through business objects and relations. In addition to my earlier blog "The power of BPath" I would like to show two more examples that use the technique of enhanceable structures and subqueries. Although the syntax might look complex at first, don't worry, I will explain it bit by bit.

Example 3: Read attributes from multiple objects using enhanceable structures

Let's say I want to read the item number, description, quantity and unit of all the items of an order. What makes it complex is that not all these attributes are contained in the BTAdminI-object but we need BTSchedISum (for the quantity) and BTProductI (for the unit) as well.

With BPath this can be achieved in one statement:

1  lv_data_ref = lo_order->get_properties_by_bpath(
2    `~*/BTOrderHeader/BTHeaderItemsExt/BTOrderItemAll${@NUMBER_INT;@DESCRIPTION;` &&
3    `*=SUB(~*/BTItemSchedlinSumExt{@ORDER_QTY});` &&
4    `*=SUB(~*/BTItemProductExt{!UNIT=@PROCESS_QTY_UNIT})}` ).

The result is:

Okay, let me explain line by line.
  1. The BPath-statement is triggered and the result is returned and captured as a data reference
  2. The "~*" stands for an Enhanceable Structure meaning that the structure is dynamically created based on the requested attributes within the Assignment Block, indicated with "{}". The attributes are prefixed with "@" and separated by ";". The "$" after BTOrderItemAll indicates that we want a table to be returned with all items, otherwise the result would be a structure based on the first item.
  3. The "*=SUB" starts a subquery to read the quantity using a relation from object BTAdminI as returned by relation BTOrderItemAll from line 1
  4. Another subquery is used to read the unit from another relation (again from BTAdminI) but this time the value is assigned using the "!" character to a field named UNIT. The assignment block is closed with "}" since this is the last field
Example 4: Read attributes from multiple objects using a DDIC-structure

One drawback of the previous example is the generic data reference. To access the data you need some field-symbols and make assumptions on the available fields in the dynamically created table. To get rid of this dynamic aspect it's possible to provide a DDIC-structure to the BPath-statement. Instead of using "~*" replace the star with the name of the DDIC-structure as shown in line 6 below. Now the result can be safely cast to a table type with ZITEM as line type (line 5) and it can be directly dereferenced to continue working with an ordinary internal table (line 9).

1  data:
2    lt_items_ref type ref to zitem_t,
3    lt_items     type zitem_t.
5  lt_items_ref ?= lo_order->get_properties_by_bpath(
6    `~ZITEM/BTOrderHeader/BTHeaderItemsExt/BTOrderItemAll${@NUMBER_INT;@DESCRIPTION;` &&
7    `*=SUB(~*/BTItemSchedlinSumExt{@ORDER_QTY});` &&
8    `*=SUB(~*/BTItemProductExt{!UNIT=@PROCESS_QTY_UNIT})}` ).
9  lt_items = lt_items_ref->*.

Enjoy BOL programming!