The stated requirement was to be able to have a KPI tile in the Fiori Launch Pad, that would show a count of the customers that had been able to reach one of two (or both) sales tiers. A sales tier being a sales volume. The numbers are not realistic, but let’s say that tier 1 would represent customers that had been able to reach a sales volume of 1000USD and tier 2 would represent customers that had been able to reach a sales volume of 2000USD. This volume had to be reached over a specific period of time, and if the customer reached one of the two tiers, over that specified period of time, he would then be eligible for a credit (or a rebate if you prefer). The higher the tier achieved, the greater the credit issued. As you can see, using standard features of S/4HANA Cloud, this was achieved quite easily.

The next requirement, was to be able to drill down to a graphical representation of these sales volumes and clearly highlight the two sales tiers (1000USD and 2000USD), so as to be able to instantaneously visualise which customer had reached these tiers. Again, as you can see below, this and much more was achieved using standard capabilities of S/4HANA Cloud. Not only were we able to build the desired graphical representation of the sales volumes and sales tiers, we also added mini charts (mini KPI’s) as well as various evaluations, so as to be able to slice and dice the information in various ways.

So far, the customer brief was met. However, thinking about the use of this data, it quickly becomes evident that the next steps to be taken on the basis of this data, is to be able to issue the due credit to the eligible customers – but how are you going to do it? From the app above, it is possible to present the data in tabular form, you could even add calculated fields in the evaluation of your KPI tile to calculate things such as the amount of the credit. You could also export the data to a spreadsheet and there also calculate the credit due. Then, you are left with the need to create the credit memo in the system. Whilst of course possible, this is fraught with risks (badly slicing the data could lead to over/under issuing credits) and of course immensely tedious – imagine someone entering a large amount of documents in the system manually (again fraught with risks). That said, thanks to S/4HANA Cloud and the power of extensibility there is a much better, faster and leaner way to do this.

What we did, was to use the ‘Custom Business Object’ in S/4HANA Cloud to define a new object – Sales Volumes tiers. The custom business object allows you to define the structure of the object that you need as well as the fields that define this object. In our case we needed fields that would pertain to customers, (customer ID, name, sales organisation…), tiers information (the volume that needed to be achieved as well as the % of the credit that would be issued). Lastly we also wanted to restrict how the volume was going to be measured. I.e was all the customer’s business going to count, or did we want to restrict by a set of materials or materials groups for example. We chose the material group. A snapshot of part of the structure of our Custom Business Object is shown below.

Once the custom business object was created, we then used some coding to realise the magic of the process, namely, to call a CDS to sum the amounts billed to the customer, and on the basis of that determine the amount of the credit to be issued to the customer (if eligible). Then we perform a second call to a whitelisted API to actually create the credit to the customer.

I will let you watch the video recording below to give you a complete view of what we did, but at a high level, in bullet point form below are the main steps/functions:

  • Creates a custom business object with the required structure and fields.
  • We added validation logic to make sure the data entered was correct, as expected and was going to play nicely with the subsequent processes
  • We added some logic, to call the CDS I_BillingDocumentItemCube so as to sum the sales volume of the considered customer.
  • One the basis of the sales volume determined, and based on the sales targets of the customer, we determine if the customer achieved the Tier 1 or Tier 2 target and if he did, we calculate the corresponding amount of the credit he earned.
  • We then make a POST call to the whitelisted API API_CREDIT_MEMO_REQUEST_SRV, to actually create the credit memo in the S/4HANA Cloud system.

Looking at these steps, you can imagine the level of automation that was achieved as well as the added value offered!


Code Snippets & how-to's

Please check out the blog from my colleague Arun here : https://blogs.sap.com/2018/06/13/mass-update-using-sap-s4hana-cloud-in-app-extensibility/ . His blog will show you what you need to do to setup the required communication arrangements. I would like to thank Arun for his wonderful blog which provided the missing pieces to mine as well as his pointers along the way.

All code provided below is provided as-is. You take full responsibility for using as it nowhere near production grade code.

Below is the code for the first action. It retrieves the sales volumes from the billing CDS and works out the credit due to the customer.

 

 

* Action Calculate for Node ID sales_volume_rebate
*
* Importing Parameter : association (Navigation to Parent/Child/Associated Node Instances)
* Changing Parameter  : sales_volume_rebate (Current Node Data)
* Exporting Parameter : message (Message with Severity S(uccess), W(arning), E(rror))

* -----------------------------------------------------------------------
* calculate bonus
*
DATA: netamount          TYPE P DECIMALS 2.
DATA: internetamount     TYPE P DECIMALS 2.
DATA: bonus1             TYPE P DECIMALS 2.
DATA: bonus2             TYPE P DECIMALS 2.



IF salesvolumestiers-releasestatus GT '2'.
        IF salesvolumestiers-isconsistent = abap_true.


"       calculate product bonus
        DATA: products  TYPE TABLE OF i_productgroup.
        DATA: product   LIKE LINE OF products.

*       get product list from sub node Products
        DATA(bonusproducts) = association->to_materialgroups( ).

        "           get net amount for product
        LOOP AT bonusproducts INTO DATA(bonusproduct).
            SELECT    *
                FROM  i_productgroup
                INTO  @product
                WHERE i_productgroup~materialgroup = @bonusproduct-materialgroups-materialgroupid.
            ENDSELECT.


            SELECT
                            FROM I_BillingDocumentItemCube( p_exchangeratetype = 'M', p_displaycurrency = 'USD' )
                            FIELDS SUM( SlsVolumeNetAmtInDspCrcy )
                            WHERE   creationdate BETWEEN @salesvolumestiers-validfrom AND @salesvolumestiers-validto
                                AND SalesOrganization = @salesvolumestiers-salesorg
                                AND soldtoparty = @salesvolumestiers-soldtoparty
                                AND materialgroup = @bonusproduct-materialgroups-materialgroupid
                            INTO @internetamount.


        netamount = internetamount + netamount.

        ENDLOOP.

        salesvolumestiers-volumeachieved_v = netamount.
        salesvolumestiers-volumeachieved_c = 'USD'.

        if netamount LT salesvolumestiers-targetrevenue1_v.
            salesvolumestiers-calculatedbonus_v = 0.
        elseif netamount GT salesvolumestiers-targetrevenue2_v.
            salesvolumestiers-calculatedbonus_v = ( ( netamount / 100 ) * ( salesvolumestiers-bonusrate2 ) ).
            salesvolumestiers-calculatedbonus_c = 'USD'.
        else.
            salesvolumestiers-calculatedbonus_v = ( ( netamount / 100 ) * ( salesvolumestiers-bonusrate1 ) ).
            salesvolumestiers-calculatedbonus_c = 'USD'.
        endif.

        salesvolumestiers-releasestatus = 4.

    ENDIF.
ENDIF.
* SAP CODE BELOW

message = VALUE #(
  severity = co_severity-success
  text     = 'Action CalculateCustomerCredit executed'
).


Then the next piece of code is that which is used create the credit memo per se. Again you will want (have to!) add your own error handling code and execution logic.

* Action CreateCreditMemo for Node ID SALESVOLUMESTIERS
*
* Importing Parameter : association (Navigation to Parent/Child/Associated Node Instances)
*                       write (API for creating and updating Custom Business Object Node Instances)
* Changing Parameter  : SALESVOLUMESTIERS (Current Node Data)
* Exporting Parameter : message (Message with Severity S(uccess), W(arning), E(rror))

CHECK cl_ble_http_client=>is_service_available(
    communication_scenario = 'YY1_INT_HTTP'
    outbound_service       = 'YY1_INT_HTTP_REST'
) = abap_true.

DATA(lo_client) = cl_ble_http_client=>create(
    communication_scenario = 'YY1_INT_HTTP'
    outbound_service       = 'YY1_INT_HTTP_REST'
).

* we get the csrf token to be able to make the POST call later
DATA(request) = cl_ble_http_request=>create( ).
DATA lv_s2 TYPE string VALUE '/API_CREDIT_MEMO_REQUEST_SRV/A_CreditMemoRequest?%24top=1&%24format=json'.
request->set_header_parameter( exporting name = 'X-CSRF-TOKEN' value = 'FETCH' ).
request->set_method( 'GET' )->set_resource_extension( lv_s2 ).
TRY .
    DATA(response) = lo_client->send( request ).
    DATA(lv_csrf) = response->get_header_parameter( 'x-csrf-token' ).
    CATCH cx_ble_http_exception INTO DATA(l5).
ENDTRY.

* We build the payload that will be submitted in the POST call
* We will also convert the calculated bonus to a string so that the call plays nicely
DATA: lv_payload TYPE string,
    calcbonus TYPE string,
    lv_pl41 TYPE string value '{"d" : { "PurchaseOrderByCustomer": "CREDIT-2018- ',
    lv_pl42 TYPE string value '","SoldToParty": "',
    lv_pl43 TYPE string value '","SalesOrganization":"',
    lv_pl44 TYPE string value '","CreditMemoRequestType":"CR","OrganizationDivision":"00","DistributionChannel":"10",',
    lv_pl45 TYPE string value '"to_Item": [ { "Material": "CREDIT","RequestedQuantity": "1","to_PricingElement": [ {',
    lv_pl46 TYPE string value '"ConditionType":"PPR0","ConditionRateValue": "',
    lv_pl47 TYPE string value '","ConditionCurrency":"',
    lv_pl48 TYPE string value '"} ] } ] } }'.

* We retrieve the calculated bonus from the custom business object
 calcbonus = salesvolumestiers-calculatedbonus_v.
* We get rid of the trailing space that is added to the calculated bonus
 CONDENSE calcbonus.

* we concatenate the various fields that will make up the payload
CONCATENATE lv_pl41 salesvolumestiers-soldtoparty lv_pl42 salesvolumestiers-soldtoparty lv_pl43
            salesvolumestiers-salesorg lv_pl44 lv_pl45 lv_pl46
            calcbonus lv_pl47
            salesvolumestiers-calculatedbonus_c lv_pl48  INTO lv_payload.

* we make the POST call to the credit memo request API
DATA(request1) = cl_ble_http_request=>create( ).
DATA lv_s1 TYPE string VALUE '/API_CREDIT_MEMO_REQUEST_SRV/A_CreditMemoRequest'.
request1->set_header_parameter( exporting name = 'x-csrf-token' value = lv_csrf ).
request1->set_content_type( 'application/json' ).
request1->set_method( 'POST' )->set_resource_extension( lv_s1 ).
request1->set_body( lv_payload ).

TRY .
    DATA(response1) = lo_client->send( request1 ).
    CATCH cx_ble_http_exception INTO DATA(lx1).
ENDTRY.

* We also want to be able to update the custom business object with the created credit memo number
* So we will use regular expressions to find the content of the  tag in the response
DATA result TYPE match_result_tab.
DATA line LIKE LINE OF result.
DATA sub TYPE submatch_result.
DATA output TYPE string.
DATA lx60 TYPE string.
output = response1->get_body( ).

FIND ALL OCCURRENCES OF REGEX '((?:[^<]|<?!d:CreditMemoRequest>)*)' IN output IGNORING CASE RESULTS result.
LOOP AT result INTO line.
   LOOP AT line-submatches INTO sub.
     lx60 = output+sub-offset(sub-length).
   ENDLOOP.
 ENDLOOP.

* We update the custom business object field Credit Memo with the credit memo number just created
 salesvolumestiers-creditmemo = lx60.

message = VALUE #(
  severity = co_severity-success
  text     = 'Credit Memo Created:'
 && lx60
 ).



Share on Social Media