Why, when and how to store IPC pricing result fields to 1Order tables using condition functions
How to avoid a common trap with performance using IPC
The IPC is calculating a lot of important such as gross value, net value, total discounts or even more derived ones like profit margin.
This information shall then be available sometimes as a search field, sometimes as a search result and often for display on the overview page of the 1Order document, be it a sales order, sales contract, service contract or any other kind of 1Order document.
Often we talk about large 1Order documents with many items. The performance problem which then arises, is, that in order to access any IPC field (in particular any IPC header field) all items need to be loaded into IPC. This is a performance intensive step which can take e.g. on the order of 10-20 seconds for a document 500 items. The time is needed to both to read the respective item information from the database into the 1Order buffers, to transfer this information to IPC and to process it within the IPC.
The important thing to note here is that for display purposes (searches, opening a document in display mode) the opening of an IPC session can in most cases be avoided. The reason is that the 1Order and IPC offer powerful and simple to use methods to store pricing result fields in the 1Order tables. From there they can be easily used for searches and for display purposes.
Before describing the details it shall be mentioned that another closely related performance trap in the usage of IPC is to implement pricing logic within the 1Order. The problem with this is that in order to even start the calculation, an IPC session needs to be opened which is costly as we just said. But then also often the conditions need to be transferred from IPC to ABAP which is costly as well (e.g. by calling function module PRC_PD_ITEM_SHOW). This is why all pricing related logic should remain on IPC, because at this point in pricing all the information is readily available.
But let us assume that the required value or rate is already calculated and we just want show it or search for it. In simple cases you do not have to do anything, because information like gross value, net value or tax value is already in table CRMD_PRICING_I (item) or CRMD_CUMULAT_H (header). Even if a field is not available on the header level, summing up the item values in ABAP is usually not a performance problem and in any case much faster than opening an IPC session. Besides this header fields with a fixed value there are also the subtotals which are stored in table CRMD_PRICING_I. They can be freely defined in the pricing procedure. As the name suggests, they allow summing a certain range of condition in the pricing procedure. As an example “Discount Amount” in the below picture is a sum of condition types with level 101 to 299.
The number of subtotals in standard is limited from to six.
There is yet another less well known way to transfer price result value to ABAP. This way is not limited in number and it is not even required to change the pricing procedure for this. This way is based on a concept called condition functions.
A condition function can be defined in IMG and then assigned to one or many condition types.
(Despite the IMG path the functionality is not service contract specific.)
To make use of a condition function it needs to be assigned to a condition function group, which needs to be assigned to an item category.
As a result of this, during pricing IPC will sum up the condition value of all conditions assigned to the condition function for a given item and provide it as a result of pricing. This means we can access any pricing information, as long as it is available as a value of a condition type.
The remaining task is now to create a new field in the CRMD_PRICING_I table (which can be done using the AET tool) and then to update it after each pricing. For this task we can look at the standard is doing it. In this way we use a method with minimal performance impact.
Standard has registered function module CRM_PRICING_I_NEW_PRICING_EC on event PRIDOC->AFTER_CHANGE, which is called after pricing. (For more information how to use 1Order event handler see this posting: https://scn.sap.com/docs/DOC-40871)
From this function module we can read off how to get the condition function value and how to populate the PRICING_I field with it.
First step is to move the event handler value to a typed variable.
DATA: ls_item_ret TYPE prct_item_ret,
ls_pricing_i_comint TYPE crmt_pricing_i_comint.
ls_item_ret = iv_strval_new.
Then we can read out the condition function value (in this case the total discount) from this structure and populate our PRICING_I communication structure.
* Transfer values to document fields
ls_pricing_i_comint-ref_guid = iv_object_guid.
READ TABLE ls_item_ret-cond_function ASSIGNING <fs_cond_function>
WITH KEY destination = 'TOTAL_DISCOUNT'. "#EC NOTEXT
IF sy-subrc EQ 0.
ls_pricing_i_comint-total_discount = <fs_cond_function>-value.
As the final step we call CRM_PRICING_I_MAINTAIN_OW and pass the value. We also have to fill the input fields with our fieldname which we want to update.
CALL FUNCTION 'CRM_PRICING_I_READ_OW'
iv_guid = iv_object_guid
es_pricing_i_wrk = ls_pricing_i_wrk
OTHERS = 0.
move-corresponding ls_pricing_i_wrk to ls_pricing_result_old.
move-corresponding ls_pricing_i_comint to ls_pricing_result_new.
* only when any field has changed
check ls_pricing_result_old <> ls_pricing_result_new.
* Put the new values to PRICING_I
CALL FUNCTION 'CRM_PRICING_I_MAINTAIN_OW'
is_pricing_i_comint = ls_pricing_i_comint
ct_input_field_names = lt_input_field_names
parameter_error = 1
error_occurred = 2
OTHERS = 3.
We have further optimized the code to perform the PRICING_I maintain call only, when new the value is different from the currently stored one.
As a result we do not need to perform an expensive opening of an IPC session anymore, when accessing this value either during searches or on the document or item overview page. Only when we start to edit the document, the opening of an IPC session can in most cases not be avoided.