The 1Order event handler is available for customers in IMG under CRM->Transactions->Basic Settings->Transactions->Edit Event Handler Table.
However the documentation is rather short.
Find here a comprehensive guide how to use 1Order Event Handler for custom enhancements.
1Order Event Handler: How to guide for customers
Note: The following document describes a very powerful way to enhance the functionality of a business transaction. Although in the formal sense the usage of the event handler is not a modification (no registry key from SAP required) the negative effects of using the event handler can be equally bad as from an actual modification. SAP is not liable for any damage caused by customer event handler entries and does not offer any standard support in case something does not work as expected or in case of unwanted functional or performance related side effects.
The 1Order event handler is a powerful tool to enhance the business functionality of the 1Order. Since the event handler operates on the API layer (inside function modules CRM_ORDER_MAINTAIN and CRM_ORDER_SAVE) it is independent of the UI. Its basic idea is not hard to understand but it requires a certain experience and 1Order knowledge to use it in the right way. Also while the event handler is very powerful a non-optimal usage of the event handler may result in a very severe performance degradation of your business application. On the other hand the right usage of the event handler allows for performance optimized implementation even of complex business logic which can be reached by no other method.
An alternative way to enhance the 1Order is through the 1Order BADIs such as CRM_ORDERADM_H_BADI, CRM_SALES_BADI, ORDER_SAVE BADI etc.). In certain cases these BADIs are simple to use. For example if you want to run certain code during the saving of the order then the ORDER_SAVE BADI is the easiest way to go (Using method check_before_save you can prevent the save and using method prepare you can make last minute changes to the document.) A good use case for the CRM_ORDERADM_H_BADI is when you have extended ORDERADM_H with your own field and you want to fill it dependent on one or several other ORDERADM_H fields. You can replace ORDERADM_H with any other objects in this example. (See below for an explanation what a 1Order object is.) Let’s now look at an example where the usage of the 1Order BADIs becomes a problem. Assume you have created a new field in CUSTOMER_I and you want to fill it dependent on the product of the specific line item. The first idea is to use the CUSTOMER_I BADI. However the CUSTOMER_I BADI is not guaranteed to run when the product has changed. Also inside the BADI you do not know if the product has changed so performance optimized programming becomes a problem. You could also consider implementing the ORDERADM_I BADI. In this case you can easily determine whether the product has changed but you have no access to the CUSTOMER_I fields within this BADI. For this use case there is no obvious way to solve it using the 1Order BADIs unless you can wait until the save and the ORDER_SAVE BADI can be used.
The SAP standard 1Order business logic is implemented using the event handler as well. This is another reason to learn how the event handler works. Note that SAP standard event handler callbacks are maintained using transaction CRMV_EVENT whereas customer specific even callbacks are registered in customizing view CRMV_EVENT_CUST. Apart from this technical difference the logic how event callbacks are registered is exactly the same for SAP standard event callbacks and for customer event callbacks.
The basic idea behind the event handler is that there are certain 1Order events on which so called callback function modules can be registered. The callback function module will be called either immediately when the event is raised or delayed depending on the execution time which is specified.
There are various fields when doing an event registration which will be explained here.
The transaction category simply defines for which kind of object the callback is relevant and should be executed. Examples for values are BUS2000115 (Sales Order) and BUS2000116 (Service Order). Value BUS20001 can be used if the callback is needed for all types of 1Order documents. (There is also the possibility in standard to influence the relevance of an event registration by customizing but this is beyond the scope of this document.)
A basic thing one need to know in order to use the 1Order event handler obviously is which events there are. For this a little background information is needed: The 1Order consists of certain so called 1Order objects. A 1Order object is basically a collection of fields which can be processed and stored together with this order. When we talk about a 1Order object we basically mean everything from the database table(s) (one for a simple objects, can be several for complex objects), the application buffer and the processing logic for this collection of field. There are flat objects which exist one time per header or item and complex objects which contain table like information (eg. partners).
A creation or change of a 1Order document in the buffer is triggered by a call of CRM_ORDER_MAINTAIN. Typical calls of CRM_ORDER_MAINTAIN can either be triggered directly by end users through a particular UI, by batch jobs or by CRM middleware. The CRM_ORDER_MAINTAIN API distributes the requested changes to the individual 1Order objects which are processing them. Further changes of individual 1Order objects are triggered indirectly by the event mechanism which is described here. For each change of a flat 1Order object there is an AFTER_CREATE (first time) or AFTER_CHANGE event raised. For many purposes it is not important to distinguish between AFTER_CREATE and AFTER_CHANGE when registering a callback for a flat object. But you need to register for both if you want to cover all changes. For complex objects there is usually a AFTER_CREATE, AFTER_CHANGE and AFTER_DELETE event. The event does not refer to the objects as a whole but to a single record of the object. For example if a new partner is added to the document an after create event is raised for that partner. As part of an AFTER_CHANGE event the before and after values (also referred to as old and new values) of the object are published. For an AFTER_CREATE event only the new value is meaningful whereas for an AFTER_DELETE event only the old value is meaningful. The old/new values allow the optimization of the callback logic by exactly checking which fields have changed (see also compression logic). The AFTER_DELETE event is also used to delete a document in the buffer (event ORDER->AFTER_DELETE) or a document item (ORDERADM_I->AFTER_DELETE).
To sum up if your business logic depends on a set of fields you need to identify the 1Order object(s ) where the fields are located and then register for the relevant change events.
There are other special events which only have a meaning in a certain context.
Used for clearing the buffer for a document (see also execution time “Initialize Document”)
Used for saving a document to the database (see also execution time “Save of Document”)
- BEFORE_CREATE, BEFORE_CHANGE, BEFORE_DELETE
The event is raised to allow callback functions to reject a certain change requested by the user. The event is only raised for special changes. The callback function can reject the change by raising an exception. Some of the BEFORE_CHANGE events such as PARTNER_BEFORE->CHANGE are called to decide whether a certain entry is ready for input and will run for every roundtrip. Please take this into account in your performance considerations.
This event can be used in combination with a unique event attribute to trigger a certain function (see example)
There are further even more special events which are beyond the scope of this document.
Each event callback function module needs to have a certain standardized interface through which information about the event is passed to the callback. For this reason it is easiest to copy a new event handler callback function from an existing one. As a general naming convention event handler callback functions end with _EC. Which parameters are actually available depends on the compression logic. The higher the compression the less parameters are available.
IV_HEADER_GUID: Header guid of the current 1Order document (always filled)
IV_OBJECT_GUID: Guid of the header or item for which the event was raised
IV_OBJECT: 1Order object , for which the event was raised
IV_EVENT: Name of the event (eg. AFTER_CHANGE)
IV_ATTRIBUT: See explanation above
IV_EXECUTION_TIME: Execution time of the event (see below)
IV_STRVAL_NEW: New values of the object (parameter has the be casted depending on the object)
IV_STRVAL_OLD: Old values of the object
The field Attribute can then be used for complex objects in the event registration to limit the registration to partner changes of a certain type (partner function type). Eg. a sold-to party has function type 0001. The possible values of the field Attribute depend on the complex object you are dealing with. For example for date changes it is the date type.
Another important field for the event handler registration is the execution time. Note that apart from some special execution times for save and initialization all execution times are part of CRM_ORDER_MAINTAIN. Event callbacks cannot be further delayed because CRM_ORDER_MAINTAIN has to bring the 1Order document from one consistent state to the next because time and number of the CRM_ORDER_MAINTAIN calls are up to the caller. For example during each CRM_ORDER_MAINTAIN call a pricing will happen (if the document is pricing relevant and a pricing relevant change happened). Otherwise the document would stay in an inconsistent state with wrong prices. This is the background for the performance recommendation to minimize the number of CRM_ORDER_MAINTAIN calls. When there is a synchronous UI it is optimal to do one CRM_ORDER_MAINTAIN call per roundtrip.
The picture shows a schematic zoom into CRM_ORDER_MAINTAIN. Events are raised by the individual 1Order objects by calling CRM_EVENT_PUBLISH_OW. The reaching of certain execution times is notified to the event handler by the general CRM_ORDER_MAINTAIN processing logic by calling function module CRM_EVENT_EXETIME_OW.
Important execution times inside CRM_ORDER_MAINTAIN are:
- End of header processing
- End of item processing
- End of document (better name would be “End of CRM_ORDER_MAINTAIN”)
Important special execution times are:
- Initialize Document
This execution time is only used in combination with the event INIT. Both are triggered inside function module CRM_ORDER_INITIALIZE. This event/execution time is used for clearing the data buffers. It must not be used for anything else.
- Save Document
This execution time is only used in combination with the event SAVE. It is triggered inside function module CRM_ORDER_SAVE. (The event SAVE is implicitly triggered by any change event such as AFTER_CREATE, AFTER_CHANGE and AFTER_DELETE). This event/execution time is intended to trigger database updates by calculating the necessary database changes and passing them to an update function module. It must not be used for anything else.
- Before Saving
This execution time is also triggered inside CRM_ORDER_SAVE and it is used to execute final changes inside the buffer. It comes before the “Save Document” execution time.
Events are always published with reference to a certain header or a certain item. In the event registration you can decide whether the registration is for header events, for item events or for both using two check boxes. This is interesting for objects which exist both on header and on item level such as partners or dates. Other objects always live either on header or on item level such as ORDERADM_H, ORDERADM_I or PRODUCT_I. In this case there is only one meaningful value for the check boxes.
A very important field for the performance tuning of the event handler is “Call Callback” which allows to implement a “compression” logic. Compression allows you to control how often the callback is called which can be very important in terms of performance.
Compression means that multiple events result in fewer (limiting case: one) callback execution. Compression can only work if the execution time is not immediately. The later the execution time is the bigger the effect of the compression can be.
There are several compression levels from no compression to maximum compression.
- Call to Header/Item, with Object, Event, Attr., Old/New Data (SPACE)
This is the option with no compression
- Call to Hdr/Item with Object/Event/attr.+ Compressed Old/New (‘K’)
Multiple events of exactly the same type for the same header/item will be compressed into one callback where the old value will be the old value of the first event and the new value is the new value of the last event.
In many cases this compression can be used in the same way as the option no compression without changes the callback code itself.
- Call to Header/Item with Object, Event, Attr. W/Out Old/New (‘A’)
Similar to the option before but with no old/new data at all
- Call to Header/Item, W/Out Object, Event, Attr.,Old/New Data (‘B’)
The callback is executed once for the header and/or each item for which the event was published
- Call Just Once Per Transaction (‘C’)
The callback is executed exactly once irrespective of how many times any event for which the callback is registered was raised and whether it was raised for the header or for different items
Using the priority you can specify the order in which callbacks for the same execution time will be executed. A higher value means a later execution. You need to worry about the priority if you depend on the result of another callback. The priority is an important field for SAP standard but often of lesser importance for customer callbacks at least at the beginning where they are not so many customer callbacks which can interfere with each other.
Implementing a call back function module it is a useful convention to name it with the ending _EC as in the standard so you can always easily identify callback function modules.
Another very important guideline to keep the performance impact of your callbacks minimal is that before running any kind of logic inside the callback function module a filtering should be done whether the event is relevant. One way to do this is to use the event information passed to the callback function through the event handler. In particular you can do an old/new comparison on field level using the old/new structures for the event. Or if the event is only relevant for a certain transaction type then you should read the current transaction type using the header guid passed to the callback function and exit the callback if it is not relevant. Especially for callback functions which are called many times you should care about how you read information. For example it is faster to read the transaction type using CRM_ORDERADM_H_READ_OB than to use CRM_ORDER_READ.
For completeness it shall be mentioned that there is an event trace available which can be activated with user parameter CRM_EVENT_TRACE. The result can be displayed using report CRM_EVENT_TRACE. You may find it useful to experiment but don’t feel discouraged if you find the rather huge output confusing. It is more important to focus on the concepts and to understand the events you care about.
General rules to follow when writing event handler callbacks:
- Never use the ABAP statements COMMIT or COMMIT WORK within a callback.
- Never trigger database updates inside a callback (exception: execution time save)
- Never call CRM_ORDER_MAINTAIN inside a callback (exception: execution time before_save). Instead call the appropriate object maintain function module to change data in the buffer (eg. CRM_ORDERADM_H_MAINTAIN_OW)
A technical knowledge of the event handler registration mechanism is not enough to make use of the event handler as a tool to implement a certain business requirement. Therefore we give some examples here.
The first example is a simple one where you want to react on product changes because you want to populate CUSTOMER_I with a certain field which is derived from the product master. Since the product guid is part of the ORDERADM_I object the registration should be for ORDERADM_I->AFTER_CREATE and ORDERADM_I->AFTER_CHANGE events. There is not much to gain to delay and/or compress the callback because the callback has to run for each product change and it is unlikely that a product change for the same item happens multiple times during one roundtrip. Of course the first thing you should do in the callback is to check whether the product guid actually changed because the callback ORDERADM_I->AFTER_CHANGE may be due to any other ORDERADM_I field which has changed.
Another example is from the SAP standard itself. It is how the pricing is implemented within the 1Order. (We actually use the old pricing interface as an example here because it is easier to understand and uses standard 1Order callbacks.). It is characteristic for the pricing that it is of certain complexity and therefore should not be executed multiple times for performance reasons. On the other hand pricing may depend on many fields in different 1Order objects so many event registrations are necessary. These characteristics are typical for other functionalities as well such as ATP check. Now since pricing should run only once per CRM_ORDER_MAINTAIN the first idea would be to register it late (“End of document”) with a high compression (“Once per transaction”). However there is a problem with this strategy. With such a registration there is no information left which items actually changed or which fields for a given item changed. This means there is no basis anymore to decide for which items pricing has to run. The solution is to use a two level event approach using two callback function modules. The first function module is CRM_PRIDOC_COM_DET_PRCTYPE_EC. It is registered on all kinds of potentially pricing relevant 1Order changes and uses no compression. This means the information which item has changed and which fields of the item have changed is available inside the callback and based on this the function module can decide whether pricing needs to run for the item or not. However CRM_PRIDOC_COM_DET_PRCTYPE_EC will not actually trigger the pricing. It will remember the item for which pricing needs to run in an internal table and publish the event PRIDOC_COM->AFTER_CHANGE for the document header. On this event function module CRM_PRIDOC_UPDATE_EC is registered with compression “Once per transaction”. This means it will be called once per CRM_ORDER_MAINTAIN and it will use internal table written by function module CRM_PRIDOC_COM_DET_PRCTYPE_EC to trigger pricing for the right items.
If you want to implement your own two level event approach you can use the object ORDER and the event TRIGGER_FUNCTION to trigger the second level event callback. In order to make sure that you do not interfere with others who are using the event TRIGGER_FUNCTION make sure that you use a unique event attribute starting with Z such as Z_MYFUNCTION. Of course you have to publish the event yourself in the first level callback function by calling function module CRM_EVENT_PUBLISH_OW.