Wednesday, 27 March 2019

Creating a draft enabled Sales Order Fiori App using the new ABAP Programming Model – Part 5: Adding determinations

Adding determinations


In the previous part we were able to provide our app with basic functionalities. By now our app should be able to:

◈ Search for Sales Orders
◈ List Sales Orders
◈ Display Sales Order details
◈ Lock Sales Orders
◈ Delete Sales Orders

In this part of the blog series we’ll focus on creating and changing Sales Orders draft instances. We’ll do that by:

◈ Adding custom determinations

     ◈ To generate a temporary Sales Order number for new draft Sales Orders
     ◈ To generate a new item number when adding an item to a draft Sales Order
     ◈ To simulate a sales order

Because we’re working with a legacy key (and not using a GUID key field), we’ll need to add custom determinations to fill the legacy key fields and to simulate a sales order.

Generating a temporary Sales Order number and setting default header data

Open up the BOPF Business Object and go to the root node.

ABAP Development, SAP Fiori for SAP S/4HANA, SAP S/4HANA, SAP ABAP Tutorial and Material

Once you are in the root node, open the determinations.

ABAP Development, SAP Fiori for SAP S/4HANA, SAP S/4HANA, SAP ABAP Tutorial and Material

Add a new determination using the “New…” button.

ABAP Development, SAP Fiori for SAP S/4HANA, SAP S/4HANA, SAP ABAP Tutorial and Material

Enter following details in the popup and click “Finish“. Afterwards activate the Business Object

ABAP Development, SAP Fiori for SAP S/4HANA, SAP S/4HANA, SAP ABAP Tutorial and Material

Open up the new determination (using CTRL+Click) and activate it only for the Create event as shown below.

ABAP Development, SAP Fiori for SAP S/4HANA, SAP S/4HANA, SAP ABAP Tutorial and Material

Open the newly generated class.

ABAP Development, SAP Fiori for SAP S/4HANA, SAP S/4HANA, SAP ABAP Tutorial and Material

Add following logic to method /BOBF/IF_FRW_DETERMINATION~EXECUTE. This determination will provide the draft instance (only in create mode) with:

◈ A fixed temporary sales order number ($000000001)
◈ A fixed order type (OR => internal TA)
◈ Created by, created on, created at
◈ Initial values (based on user parameters) for sales organization, distribution channel and division

METHOD /bobf/if_frw_determination~execute.
  DATA lt_data TYPE ztsdi_soheader.

* Read data with the given keys
  io_read->retrieve(
    EXPORTING
      iv_node       = is_ctx-node_key   " uuid of node name
      it_key        = it_key            " keys given to the determination
    IMPORTING
      eo_message    = eo_message        " pass message object
      et_data       = lt_data           " itab with node data
      et_failed_key = et_failed_key     " pass failures
  ).

* Assign numbers to each newly created line and tell BOPF about the modification
  LOOP AT lt_data REFERENCE INTO DATA(lr_data) WHERE vbeln IS INITIAL.

    lr_data->vbeln = '$000000001'.
    lr_data->auart = 'TA'.
    lr_data->erdat = sy-datum.
    lr_data->ernam = sy-uname.
    lr_data->erzet = sy-uzeit.

    GET PARAMETER ID 'VKO' FIELD lr_data->vkorg.
    GET PARAMETER ID 'VTW' FIELD lr_data->vtweg.
    GET PARAMETER ID 'SPA' FIELD lr_data->spart.

    io_modify->update(
      EXPORTING
        iv_node           = is_ctx-node_key    " uuid of node
        iv_key            = lr_data->key    " key of line
        is_data           = lr_data            " ref to modified data
        it_changed_fields = VALUE #( ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soheader-vbeln )
                                     ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soheader-auart )
                                     ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soheader-vkorg )
                                     ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soheader-vtweg )
                                     ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soheader-spart )
                                     ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soheader-erdat )
                                     ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soheader-ernam )
                                     ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soheader-erzet ) )
    ).

  ENDLOOP.

ENDMETHOD.

Generating a Sales Order item number based on customizing

When creating a new Sales Order item a new item number needs to be generated. For this we’ll need to create a new determination on the item node of the BOPF Business Object. To do so, open the item node and switch to the determination view.

ABAP Development, SAP Fiori for SAP S/4HANA, SAP S/4HANA, SAP ABAP Tutorial and Material

Add a new determination using the “New…” button.

ABAP Development, SAP Fiori for SAP S/4HANA, SAP S/4HANA, SAP ABAP Tutorial and Material

Enter following details in the popup and click “Finish“. Afterwards activate the Business Object.

ABAP Development, SAP Fiori for SAP S/4HANA, SAP S/4HANA, SAP ABAP Tutorial and Material

Open up the new determination (using CTRL+Click) and activate it only for the Create event as shown below.

ABAP Development, SAP Fiori for SAP S/4HANA, SAP S/4HANA, SAP ABAP Tutorial and Material

Add following logic to method /BOBF/IF_FRW_DETERMINATION~EXECUTE of the newly generated class. This determination will provide the draft instance with logic to:

◈ Determine the highest item number in the draft instance
◈ Read the item interval from customizing
◈ Increment the item number with the item interval in customizing

METHOD /bobf/if_frw_determination~execute.
  DATA: lt_header_data TYPE ztsdi_soheader,
        lt_item_data   TYPE ztsdi_soitem,
        lt_header_key  TYPE /bobf/t_frw_key.

* Read data with the given keys (item data)
  io_read->retrieve(
    EXPORTING
      iv_node       = is_ctx-node_key   " uuid of node name
      it_key        = it_key            " keys given to the determination
    IMPORTING
      eo_message    = eo_message        " pass message object
      et_data       = lt_item_data      " itab with node data
      et_failed_key = et_failed_key     " pass failures
  ).

* Read root key to determine header data
  READ TABLE lt_item_data INTO DATA(ls_item_data) INDEX 1.
  lt_header_key = VALUE #( ( key = ls_item_data-root_key ) ).

* Read header data based on root key
  io_read->retrieve(
    EXPORTING
      iv_node       = zif_sd_i_soheader_c=>sc_node-zsd_i_soheader   " uuid of node name
      it_key        = lt_header_key   " keys given to the determination
    IMPORTING
      eo_message    = eo_message        " pass message object
      et_data       = lt_header_data    " itab with node data
      et_failed_key = et_failed_key     " pass failures
  ).

* Read header data
  READ TABLE lt_header_data INTO DATA(ls_header_data) INDEX 1.

* Get highest value of position number in draft table
  SELECT MAX( posnr ) FROM zsd_soitem_d
    INTO @DATA(lv_posnr)
    WHERE parentdraftuuid = @ls_header_data-key.
  "WHERE vbeln = @ls_header_data-vbeln.

* Get item interval from order type customizing
  SELECT SINGLE incpo FROM tvak
    INTO @DATA(lv_increment)
    WHERE auart = @ls_header_data-auart.

* Assign numbers to each newly created line and tell BOPF about the modification
  LOOP AT lt_item_data REFERENCE INTO DATA(lr_item_data) WHERE posnr IS INITIAL.
    ADD lv_increment TO lv_posnr.

    lr_item_data->vbeln = ls_header_data-vbeln.
    lr_item_data->posnr = lv_posnr.
    lr_item_data->erdat = sy-datum.
    lr_item_data->ernam = sy-uname.
    lr_item_data->erzet = sy-uzeit.

    io_modify->update(
      EXPORTING
        iv_node           = is_ctx-node_key    " uuid of node
        iv_key            = lr_item_data->key    " key of line
        is_data           = lr_item_data            " ref to modified data
        it_changed_fields = VALUE #( ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soitem-vbeln )
                                     ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soitem-posnr )
                                     ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soitem-erdat )
                                     ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soitem-ernam )
                                     ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soitem-erzet ) )
    ).

  ENDLOOP.

ENDMETHOD.

Simulating a sales order


When changing a field (either on header or item level) we’ll need to recalculate the order value. For this we’ll create an additional determination that is triggered when changing both the header and item nodes of the business object.

Add a new determination on the root node (same steps as in the first part of this blog) and enter following details in the popup.

ABAP Development, SAP Fiori for SAP S/4HANA, SAP S/4HANA, SAP ABAP Tutorial and Material

In the determination screen add the item node using the “New ” button.

ABAP Development, SAP Fiori for SAP S/4HANA, SAP S/4HANA, SAP ABAP Tutorial and Material

Enable the determination for both nodes on update.

ABAP Development, SAP Fiori for SAP S/4HANA, SAP S/4HANA, SAP ABAP Tutorial and Material

Add following logic to method /BOBF/IF_FRW_DETERMINATION~EXECUTE of the newly generated class. This determination will provide the draft instance with logic to:

◈ Simulate a sales order with the changes in the draft instance
◈ Pass errors in simulation to the Fiori App
◈ Recalculate the item and header price of the sales order

METHOD /bobf/if_frw_determination~execute.
  DATA: lt_header_data TYPE ztsdi_soheader,
        lt_item_data   TYPE ztsdi_soitem.

  DATA: ls_header    TYPE bapisdhead,
        lt_items_in  TYPE TABLE OF bapiitemin,
        lt_items_out TYPE TABLE OF bapiitemex,
        lt_partners  TYPE TABLE OF bapipartnr,
        lt_messages  TYPE bapiret2_t.

* Read header data with the given keys
  io_read->retrieve(
    EXPORTING
      iv_node       = is_ctx-node_key   " uuid of node name
      it_key        = it_key            " keys given to the determination
    IMPORTING
      eo_message    = eo_message       " pass message object
      et_data       = lt_header_data   " itab with node data
      et_failed_key = et_failed_key    " pass failures
  ).

  READ TABLE lt_header_data INTO DATA(ls_header_data) INDEX 1.

* Read item data with the given keys
  io_read->retrieve_by_association(
    EXPORTING
      iv_node                 = is_ctx-node_key
      it_key                  = it_key
      iv_association          = zif_sd_i_soheader_c=>sc_association-zsd_i_soheader-_items
      iv_fill_data            = abap_true
    IMPORTING
      eo_message              = eo_message
      et_data                 = lt_item_data
      et_failed_key           = et_failed_key
  ).

* Set sales order header data
  ls_header = VALUE #( doc_type = ls_header_data-auart
                       sales_org = ls_header_data-vkorg
                       distr_chan = ls_header_data-vtweg
                       division = ls_header_data-spart
                       sales_off = ls_header_data-vkbur
                       sales_grp = ls_header_data-vkgrp
                       purch_no = ls_header_data-bstnk ).

* Set sales order partners
  lt_partners = VALUE #( ( partn_role = 'AG' partn_numb = ls_header_data-kunnr )
                         ( partn_role = 'WE' partn_numb = ls_header_data-kunwe ) ).

  LOOP AT lt_item_data INTO DATA(ls_item_data).

    APPEND VALUE #( itm_number = ls_item_data-posnr
                    material = ls_item_data-matnr
                    req_qty = ls_item_data-kwmeng * 1000
                    sales_unit = ls_item_data-vrkme
                    short_text = ls_item_data-arktx ) TO lt_items_in.

  ENDLOOP.

* Simulate sales order
  CALL FUNCTION 'BAPI_SALESORDER_SIMULATE'
    EXPORTING
      order_header_in = ls_header
*     convert_parvw_auart = space
    TABLES
      order_items_in  = lt_items_in
      order_partners  = lt_partners
      order_items_out = lt_items_out
      messagetable    = lt_messages.

  IF line_exists( lt_messages[ type = 'E' ] ).
*   Create message object
    eo_message = /bobf/cl_frw_factory=>get_message( ).

*   Pass messages from BAPI to BOPF framework
    LOOP AT lt_messages INTO DATA(ls_message).
      eo_message->add_message(
        EXPORTING
          is_msg       = VALUE #( msgty = ls_message-type
                                  msgid = ls_message-id
                                  msgno = ls_message-number
                                  msgv1 = ls_message-message_v1
                                  msgv2 = ls_message-message_v2
                                  msgv3 = ls_message-message_v3
                                  msgv4 = ls_message-message_v4 )
      ).

    ENDLOOP.


  ELSE.

    CLEAR: ls_header_data-waerk,
           ls_header_data-netwr.

*   Set item data
    LOOP AT lt_item_data REFERENCE INTO DATA(lr_item_data).

      READ TABLE lt_items_out INTO DATA(ls_item_out) WITH KEY itm_number = lr_item_data->posnr.
      IF sy-subrc = 0.

        lr_item_data->matnr = ls_item_out-material.
        lr_item_data->arktx = ls_item_out-short_text.
        lr_item_data->kwmeng = ls_item_out-req_qty.
        lr_item_data->vrkme = ls_item_out-sales_unit.
        lr_item_data->netwr = ls_item_out-net_value / 100.
        lr_item_data->waerk = ls_item_out-currency.


        io_modify->update(
          EXPORTING
            iv_node           = zif_sd_i_soheader_c=>sc_node-zsd_i_soitem  " uuid of node
            iv_key            = lr_item_data->key    " key of line
            is_data           = lr_item_data            " ref to modified data
            it_changed_fields = VALUE #( ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soitem-matnr )
                                         ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soitem-arktx )
                                         ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soitem-kwmeng )
                                         ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soitem-vrkme )
                                         ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soitem-netwr )
                                         ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soitem-waerk ) )
        ).

        ADD ls_item_out-net_value TO ls_header_data-netwr.
        ls_header_data-waerk = ls_item_out-currency.

      ENDIF.

    ENDLOOP.

*   Set header data
    LOOP AT lt_header_data REFERENCE INTO DATA(lr_header_data).

      lr_header_data->netwr = ls_header_data-netwr / 100.
      lr_header_data->waerk = ls_header_data-waerk.

      io_modify->update(
        EXPORTING
          iv_node           = zif_sd_i_soheader_c=>sc_node-zsd_i_soheader  " uuid of node
          iv_key            = lr_header_data->key    " key of line
          is_data           = lr_header_data            " ref to modified data
          it_changed_fields = VALUE #( ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soheader-netwr )
                                       ( zif_sd_i_soheader_c=>sc_node_attribute-zsd_i_soheader-waerk ) )
      ).

    ENDLOOP.

  ENDIF.
ENDMETHOD.

Our business object is now ready to handle draft instances of Sales Orders. In the next part we’ll handle the conversion of the draft instance into a real Sales Order.

No comments:

Post a Comment