Friday, 24 November 2017

Find your way to the BOPF

I set a learning path to catch up with SAP development tools and techniques that in spite of being around for a while now,  the comfort zone was just too comfortable to step out, until the breakthrough came in and I realized I didn’t want to be left behind. After one year things are starting to pay off now, which is great.

In this opportunity, I want to talk about the Business Object Processing Framework (BOPF), which is yet another way of maintaining database records in an Object-Oriented way. Although this is a rather simplistic view, as it turns out that the framework is capable of doing many more things, such as dealing with authorizations, or even orchestrate the application business logic flow. It can be integrated with several UI technologies, say UI5 or Web Dynpro/FPM.

Following my last examples of modeling a Music Store inside SAP, I wanted to see how could I model this into a Business Object. So I ran my VM instance with the Developer Edition of SAP AS ABAP 751, opened Eclipse and created a new business object

Eclipse has this wizard ready to create BOs

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Guides

I select the package and gave it a name. I’m taking a manual creation here, but we will see later on that is also possible to create BOs from ABAP CDS using some annotations, I’m curious on what is the end result, so I named this “MANUAL” and later on will compare this BO with one created from CDS to see the difference.

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Guides

In the next screen, you are asked to define a persistent structure and a transient structure. The Persistent structure will be the definition of the database table, the transient structure are just fields that can be calculated at runtime, like master data details that you don’t wanna have duplicated in your database tables. For the sake of simplicity, I choose to create only a Persistent structure.

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Guides

One very nice thing about ABAP 7.50 is that now we can define structures, data elements and domains from within Eclipse itself, bye bye SE11!

So my structure looks like this:

@EndUserText.label : 'Structure'
@AbapCatalog.enhancementCategory : #NOT_EXTENSIBLE
define type zinstruments_ps {
  @EndUserText.label : 'Serial number of the instrument'
  serialnumber  : abap.char(10);
  @EndUserText.label : 'Company name of the instrument'
  builder       : abap.char(35);
  @EndUserText.label : 'Type of instrument'
  type          : abap.char(30);
  @EndUserText.label : 'Image of instrument'
  picture_url   : abap.char(255);
  @EndUserText.label : 'Price of the instrument'
  @Semantics.amount.currencyCode : 'zinstruments_ps.currency_code'
  price         : abap.curr(15,3);
  @EndUserText.label : 'Currency code'
  currency_code : abap.cuky;
}

You don’t need to remember all of these annotations, there is code completion for each one of them.

The last step is to create a so-called Constants Interface. This is an auto-generated ABAP interface created by the framework, since as we will see shortly, the framework generates 32 characters (256 bits) hexadecimal identifiers, rather than plain number ranges or incremented integers. This interface makes easy to identify the BO

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Guides

By clicking on the link Go to the ROOT node, an overview window displays what we can achieve with this BO

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Guides

Here we can see the persistent structure defined in the wizard, also a combined structure, which is autogenerated by combining the persistent and transient structure. We also see a table type for this structure, and finally the database table that will be generated upon the activation of the BO. So by clicking the activate button, the database table will look like this:

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Guides

Note that we didn’t define any DB_KEY field in the structure, it was inserted by the framework, every database table created via the BOPF will have this field marked as part of the key.

Now, going back to the BO overview -not the rote node overview, we can navigate to the nodes.

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Guides
Business objects are organized in a hierarchical fashion through these nodes. At design time, we can think of a parent node and a child node as a relationship between Header and Item tables, for example. At runtime, nodes behave like internal tables grouping these instances together, and for which we can perform operations. In this case, I created a second BO, with a separate database table for managing the descriptions of my main instruments table.

OK, now that we have modeled our BOs, it’s time to play around with them through some ABAP code. We can manipulate the business objects through the BOPF API, that is composed of mainly 3 interfaces: The Service manager, that allow us to communicate with node elements, so we can execute queries, navigate through the hierarchy, perform actions, etc. There’s also the Transaction Manager, we use this interface to commit changes to the database, or even rollback changes. Then there is the configuration interface, which at this point I’m not entirely sure how to use it, but it seems that it is used to get the metadata from a BO -If anyone knows how to use it, I’m all ears.

  data: root_node_combined_structure type ref to zinstruments_cs,
        texts_combined_structured    type ref to zbo_s_instruments_texts.

  data modifications type /bobf/t_frw_modification.

  try.
      data(transaction_manager) = /bobf/cl_tra_trans_mgr_factory=>get_transaction_manager( ).
      data(service_manager) = /bobf/cl_tra_serv_mgr_factory=>get_service_manager( zif_bo_instruments_manual_c=>sc_bo_key ).
      data(object_configuration) = /bobf/cl_frw_factory=>get_configuration( zif_bo_instruments_manual_c=>sc_bo_key ).
    catch /bobf/cx_frw.
      "Are you sure the Business Object is active?
  endtry.

  "Create order root node
  create data root_node_combined_structure.
  root_node_combined_structure->key = /bobf/cl_frw_factory=>get_new_key( ).
  root_node_combined_structure->builder   = 'Fender'.
  root_node_combined_structure->serialnumber = 'FOO123'.
  root_node_combined_structure->currency_code = 'EUR'.
  root_node_combined_structure->price = '100'.
  root_node_combined_structure->type = 'electric'.

So I start by getting some instances from the BOPF API factory class, note that for the Service Manager and object configuration, I pass the bo key that the framework generated for me in the constants interface, it feels nicer to refer to it as ZIF_BO=>SC_BO_KEY rather than 0800279DF3981ED7B18B0BBFE6C68691.

Then I create my root node structure, get a new key from the API factory, and populate my fields.

append initial line to modifications assigning field-symbol(<modification>).
  <modification>-node = zif_bo_instruments_manual_c=>sc_node-root.
  <modification>-change_mode = /bobf/if_frw_c=>sc_modify_create.
  <modification>-key = root_node_combined_structure->key.
  <modification>-data = root_node_combined_structure.

Here I just say that I’m going to perform a new modification of type MODIFY_CREATE, and pass the attributes created before.

 "Create Instrument texts
  create data texts_combined_structured.
  texts_combined_structured->key = /bobf/cl_frw_factory=>get_new_key( ).
  texts_combined_structured->language_code = sy-langu.
  texts_combined_structured->name = |Instrument { root_node_combined_structure->serialnumber }|.

Here I do the same thing but with the child node -Remember that I set a separate BO to store the texts.

  append initial line to modifications assigning <modification>.
  <modification>-node = zif_bo_instruments_manual_c=>sc_node-instruments_texts.
  <modification>-change_mode = /bobf/if_frw_c=>sc_modify_create.
  <modification>-source_node = zif_bo_instruments_manual_c=>sc_node-root.
  <modification>-association = zif_bo_instruments_manual_c=>sc_association-root-instruments_texts.
  <modification>-source_key  = root_node_combined_structure->key.
  <modification>-key = texts_combined_structured->key.
  <modification>-data = texts_combined_structured.

This next step is to append the child node to the modifications table, and create an association between them. So when I save, I will have a record in the Instrument table and another one in the texts table.

Now it’s time to save these changes in the memory, prior to commit the changes in the database.

  service_manager->modify( exporting it_modification = modifications    " Changes
                           importing eo_change       = data(change_object)     " Interface of Change Object
                                     eo_message      = data(message) ).   " Interface of Message Object

The last step is to commit our changes to the database, if there were no errors.

  "Check results
  if change_object->has_failed_changes( ) = abap_false.
    transaction_manager->save( importing eo_message  = data(messages)
                               ev_rejected = data(change_rejected) ).
  endif.

And that’s it! I’ve got a record in the texts table

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Guides

And another one in the Instruments table.

SAP ABAP Development, SAP ABAP Tutorials and Materials, SAP ABAP Guides

Next time I will be working through the process of querying these tables through the API.

No comments:

Post a Comment