Enterprise Resource Planning Blogs by SAP
Get insights and updates about cloud ERP and RISE with SAP, SAP S/4HANA and SAP S/4HANA Cloud, and more enterprise management capabilities with SAP blog posts.
cancel
Showing results for 
Search instead for 
Did you mean: 
pascalrenet
Product and Topic Expert
Product and Topic Expert
Following on from the excellent blog written by my colleague harshadm, in which he describes a method to control the low margin of sales orders using workflow capabilities, I wanted to follow on and offer a different spin on this issue, that several businesses face.

To be more specific, this blog will differ, in that we will focus on controlling the margin at the sales order item level and we will leverage the alternate price determination functionalities to determine the margin and throw an error message if the margin is too low. This will still allow you to save the document but will be considered incomplete (hence no transfer of requirements). This method is also different in that we enforce a company policy - thus requiring the person entering the sales order to do what is needed (eg reduce discounts..) to keep the margin within acceptable minimums.

Foreword


As a summary, what we will cover in this blog:

  • Custom logic: We will leverage a pricing BADI, in which we will code an alternative calculation of a condition-amount determination, that we will subsequently use in our sales pricing procedure.

  • Manage your solution / CBC: We will need to make some configuration settings.

    • Register our BADI as an alternative condition calculation amount

    • Create a 'Sales Margin' pricing condition.

    • Amend our pricing procedure to reference the above pricing condition and alternative calculation




Lastly, whilst we will start off by hard coding the minimum sales order margin, this is not very flexible. I am a firm believer that business rules should be the responsibility of the business users to own and maintain. So, in line with this thought, we will show you how you can leverage a Custom Business Object (CBO) to house these business rules.

So let's get going.

Create your custom logic


It might sound odd to start here but that is what will do and I'm sure the rationale for that will become evident in the subsequent steps.

Go to the App "Custom Logic", and make sure the tab "Custom Logic" is selected.


Custom Logic


Then click "+" to add your own logic. When you add an extension, you need to indicate the appropriate business context and BAdI. In this case, we are, as shown below, interested in :

  • The Business Context of "Pricing"

  • The BAdI with description: Define Alternative Calculation of Condition Amount in Document Item

  • Enter an Implementation Description of your choice



BAdI Context


 

The next thing you will want to do is add your own code, by creating a draft. Make sure you read the documentation and you can leverage the example code as a starting point.

I am below adding the entire code I used (not production ready!).
*   Mandatory step for all implementations:
* Move the importing parameters to the changing parameters.
MOVE-CORRESPONDING item_amounts TO item_result_amounts.
MOVE-CORRESPONDING item_attributes TO item_result_attributes.
MOVE-CORRESPONDING item_quantities TO item_result_quantities.
MOVE-CORRESPONDING prcg_element_attributes TO prcg_element_result_amounts.

* the below two lines are our code. We are essentially comparing the item cost with the net
* amount to construct the margin. One line updates the amount, and the other the ratevalue

TRY.
prcg_element_result_amounts-conditionamount = ( ( 1 - ( item_amounts-costamount / item_amounts-netamount ) ) * 100 ) .
prcg_element_result_amounts-conditionratevalue = ( ( 1 - ( item_amounts-costamount / item_amounts-netamount ) ) * 100 ) .

* If the margin is less than 30% - here we have hard code the margin
* If the margin does fall below, we issue a status message to the user


IF prcg_element_result_amounts-conditionratevalue < 30.
* it is possible to raise a message if an issue is identified
pricing_message = 'Check the Margin of your Order Item'.
* indicate a pricing error if required
item_result_attributes-pricinghaserror = abap_true.
ENDIF.
CATCH cx_sy_conversion_error cx_sy_arithmetic_error INTO DATA(lx_conversion).

prcg_element_result_amounts-conditioninactivereason = 'X'.
prcg_element_result_amounts-conditionamount = 0.
* You should use the statement "RAISE EXCEPTION" only for critical business errors or programming runtime errors.
RAISE EXCEPTION TYPE cx_ble_runtime_error
EXPORTING
previous = lx_conversion.

ENDTRY.

Save your draft and test your logic. If it works as expected, then go to the "filter" tab and add a formula. I.e give a name to your logic, as shown below.


Provide a name to your logic


Now make a note of your formula name (as you will need it later) and publish your logic!

 

Configure your solution


Depending on your system, go to Manage your Solution or CBC.

Custom Adaptation for Pricing


Go to the SSCUI "Set Alternative Calculation of Condition Amount" (Id 102430). Here we will need to reference our custom logic. To do so, proceed as in the two screenshots below.

Make sure you give a routine number that is not in the SAP namespace (here I used 3000000). Make a note of your chosen number as we will need it later.


Register your formula


Then go to the detail screen level and here reference the name you had given the formula in the BAdI.


Reference your BAdI Formula name


 

Add a Pricing Condition


This step is not mandatory, but I wanted to be able to show a % amount for the margin to the sales person entering the sales order rather than the defaulted currency and amount, which could be confusing to the user.

For this, still in configuration go to the SSCUI "Set Condition Types for Pricing" (Id 101120). There is nothing out of the ordinary for this pricing condition, except that it has a calculation type A - Percentage.


Addition of a % based price condition



Adapt the pricing procedure


Before we leave the configuration side, we need to amend the pricing procedure used by our document and add to it our pricing condition as well as our alternate condition-amount determination routine, as shown below. Note that we have referenced our routine (3000000) with our pricing condition - ie our logic will supply the calculated value to the pricing condition.


Amendment of your pricing procedure



Putting it all together


Now that we have made the required settings, let's create a sales order to see how things pan out.

As you can see below, the sales person used his discretionary right to give the customer a discount of 20% (Condition YK07). Unfortunately, this has had the effect of pushing the margin of this sales order item down to 14.926%, which is below the minimum of 30%.  This results in the Status Message "Check the Margin of your Order' being advertised.


Order item below the minimum acceptable margin


The user can still save their order, but it will be considered incomplete.


Document incompletion notification


 

Before we leave...


As I said early on in this blog, I believe that business rules should be owned and maintained by the business, and what I have shown above, whilst it might do the trick is not flexible, nor scaleable and would require the involvement of a programmer to change your code whenever your business rules changed.

To introduce this idea of business rules, we are introducing a new object to the setup: A Custom Business Object (CBO). This custom object will be used to define the variables that make up the business rules and then an auto-generated UI will be used by the users to enter these rules. For example, you could have rules that are influenced by the sales organisation, the document type, the customer, the product, a promotion, etc...you might also have scenarios where you have free goods (100% discount) where a margin = 0 has to be accepted.

In the example below, we have created a CBO to illustrate how this could work. To keep it simple the only variable we will use is the sales organisation. To that effect, our CBO has been defined with the following fields. The important fields are the sales organisation and the margin. I have also highlighted the technical name of the CBO, as a CDS that we will need later will be generated when we publish our CBO.

.


Fields in our CBO


Once we have published our CBO and activated the check box to generate a UI, we can enter the business rule entries.

As you can see we have different margins, depending on the sales organisation of the order item.


Business rules data


 

Lastly, we need to revisit our custom logic and replace the hard-coded margin, and instead, read it from our CBO.

Our new logic, now looks like this.
* We are now declaring a new variable ZMARGIN 
DATA : lv_text TYPE String,
ZMARGIN TYPE p LENGTH 4 DECIMALS 2.


* Mandatory step for all implementations:
* Move the importing parameters to the changing parameters.
MOVE-CORRESPONDING item_amounts TO item_result_amounts.
MOVE-CORRESPONDING item_attributes TO item_result_attributes.
MOVE-CORRESPONDING item_quantities TO item_result_quantities.
MOVE-CORRESPONDING prcg_element_attributes TO prcg_element_result_amounts.

* the logic to determine the margin does not change

TRY.

prcg_element_result_amounts-conditionamount = ( ( 1 - ( item_amounts-costamount / item_amounts-netamount ) ) * 100 ) .
prcg_element_result_amounts-conditionratevalue = ( ( 1 - ( item_amounts-costamount / item_amounts-netamount ) ) * 100 ) .

* Here out logic changes, we are now querying our custom business object YY1_MARGINALLOWANCERULES
* to obtain the margin amount where the sales organisation of the order item = the CBO entry
* we then put the found result in the variable ZMARGIN
* the CBO and CDS have the same name --> YY1_MARGINALLOWANCERULES

SELECT SINGLE margin FROM YY1_MARGINALLOWANCERULES into @ZMARGIN
Where salesorganisation = @item_attributes-salesorganization.

* The below also changes as we are now comparing the margin to our variable ZMARGIN

IF prcg_element_result_amounts-conditionratevalue < ZMARGIN.
* it is possible to raise a message if an issue is identified
pricing_message = 'Check the Margin of your Order'.
* indicate a pricing error if required
item_result_attributes-pricinghaserror = abap_true.
ENDIF.
CATCH cx_sy_conversion_error cx_sy_arithmetic_error INTO DATA(lx_conversion).

prcg_element_result_amounts-conditioninactivereason = 'X'.
prcg_element_result_amounts-conditionamount = 0.
* You should use the statement "RAISE EXCEPTION" only for critical business errors or programming runtime errors.
RAISE EXCEPTION TYPE cx_ble_runtime_error
EXPORTING
previous = lx_conversion.

ENDTRY.

Please note that you could also introduce the use of a CBO in the scenario demonstrated by Harshad in his blog.

In closing


Whilst this blog could not be used as is, it has hopefully given you some ideas on how you could implement such a margin control mechanism in your sales orders. It has also hopefully demonstrated the flexibility of SAP S/4HANA Cloud, and maybe you will come up with a different method to tackle this issue - if you do please share your knowledge with the community!

Don't forget to transport (export/import) your extensibility objects!

Feel free to leave your feedback or questions in the comment section below.