How to implement a BAdI And How to Use a Filter – Part 4 of the Series on the New Enhancement Framework
This is part 4 of a series on the new Enhancement Framework. You find the other weblogs of this series here:
In the last weblog we defined a BAdI, provided a fallback class, instantiated the BAdI and called a BAdI method. Of course, we also provided an enhancement spot, because our BAdI, as every BAdI, needs a container to live in.
As you have learned, the fallback class is chosen in case no BAdI implementation is available. As we have not created a BAdI implementation so far, the method implementation of the fallback class was used in our example code. To change this, we create a BAdI implementation in this weblog. As soon as there is a suitable BAdI implementation the methods of the fallback class are not used any longer.
First let us have a short look at the entities we have so far: There is the enhancement spot z_es_calc_tax in which the BAdI z_badi_calc_vat lives. The BAdI interface defines an important part of the BAdI identity, insofar it defines the BAdI methods that can be used. Our interface z_if_calc_vat has one method get_vat( ).
The definition and the implementation of BAdIs are similar in one respect: Just as you need a container that is an enhancement spot for a BAdI, you cannot build a BAdI implementation unless you have the relevant container first. The relevant container type for BAdI implementations is called (simple) enhancement implementation. A simple enhancement implementation can keep many different BAdI implementations, but with one restriction:
A simple enhancement implementation is uniquely assigned to an enhancement spot. That means: A (simple) enhancement implementation can keep only BAdI implementations of BAdIs that belong to the spot the simple enhancement implementation is assigned to. This has the consequence: An (simple) enhancement implementation cannot keep BAdI implementations that implement BAdIs belonging to different spots.
This being so, we have first to create a container that is uniquely assigned to the enhancement spot our BAdI belongs to. Despite the fact that we have to do something pretty complex, the respective tool in the SE80 has paved a smooth way to do this. All you have to do is to press one button. This button is marked with an orange frame in the screenshot below:
We get to the dialog window, where we can create a (simple) enhancement implementation:
The next dialog window opens:
What are we asked to type in here? We have created so far a container for BAdI implementations, that is a (simple) enhancement implementation. This container is uniquely assigned to our enhancement spot. Once this connection is established, we can create a BAdI implementation for the BAdI within the enhancement spot. As we have defined only one BAdI within the enhancement spot, we have no choice. If we had more BAdIs in our enhancement spot, this would be the point where to select which BAdI we want to implement. As matters stand, we type in z_bdi_calc_vat_us as the name of the BAdI implementation, confirm and save the enhancement spot in the next window. There we are: This is (simple) enhancement implementation that contains the BAdI implementation z_bdi_calc_vat_us:
Obviously, the appearance of a (simple) enhancement implementation in the tool is pretty much like the one of an enhancement spot: Under the tab Enh. Implementation Elements there is a tree with the BAdI implementation(s) contained on the right-hand side. On the left, you see the properties of the marked BAdI implementation.
Note: Select the property “Implementation is active” under the header “runtime behavior”. If you do this the text below changes to: “Implementation is called”. That is intended to help you understand what the selection you have just made is for. Farther below, there is a list that shows the properties of the BAdI definition our BAdI implementation is assigned to.
To better understand the structure in which our objects are imbedded, let us have a look at this sketch:
At the top, there is the definition part: In principle, such a spot can contain many different BAdI definitions. Our enhancement spot contains only one BAdI definition. We have created a container for the BAdI implementations. This (simple) enhancement implementation is uniquely assigned to the enhancement spot, but one enhancement spot can have many enhancement implementations assigned to it. We have a one-to-many relationship between the enhancement spot and the enhancement implementation. With this relationship established, we have assigned a BAdI implementation to our BAdI. Again, this is a one-to-many relationship: A BAdI definition can have many BAdI implementations, but a BAdI implementation uniquely belongs to a BAdI.
Only if the relationship between the containers that is the enhancement spot and the enhancement implementation is established, you can assign a BAdI implementation to a BAdI definition. I have tried to show this in the picture by the large orange pipe that contains the small grey pipe.
You should keep in mind: When implementing a BAdI, you have first to establish a connection between the relevant containers. Based on this connection you can go on and implement the BAdI. This principle applies to source code plug-ins in an analogous fashion.
Now that we have a BAdI implementation, we need an implementing class. We click the triangle in front of the name of the BAdI implementation in the tree and next double-click Implementing Class:
It works the same way as with the creation of the fallback class in the last weblog. As we do not want to reuse an existing class we enter the name z_cl_calc_vat_us and select the Change icon. Again, we get to the Class Builder where the relevant interface methods are already defined. In our case it is only the method: get_vat( ).
We implement it:
DATA: percent type p value 4 . ex_amount_vat = im_amount * percent / 100 . ex_percent_vat = percent .
We save and activate the class. If we have not activated the (simple) enhancement implementation before, we have to activate it now. Now, we return to the program:
DATA: handle TYPE REF TO z_badi_calc_vat, sum TYPE p, vat TYPE p, percent TYPE p. sum = 50. GET BADI handle. CALL BADI handle->get_vat EXPORTING im_amount = sum IMPORTING ex_amount_vat = vat ex_percent_vat = percent. WRITE: 'percentage:', percent, 'VAT:', vat.
We run the program, and what do we get? The result is a percentage of four percent. And this is because the fallback class is not selected, if we have an active BAdI implementation.
As a next step, we create another BAdI implementation, this time with the VAT rate of Great Britain. To make our example more like real world programming, we create another (simple) enhancement implementation, that is another container. This is because implementing the taxes for different countries most probably belongs to different projects and because the structure of the (simple) enhancement implementations should mirror the project structure.
We navigate to our enhancement spot and use the same button as we have done above. We name the simple enhancement implementation z_ei_bad_calc_gb, the BAdI implementation z_bdi_calc_vat_gb, and the implementing class Z_BDI_CALC_VAT_GB. The implementation of the method get_vat is the same as for the USA except for the VAT rate, which is 16,5% now. After saving and activating the enhancement implementation and the class we return to the program and run it again.
This time we get a short dump with the exception cx_badi_multiply_implemented. Remember: We have defined our BAdI as single use by deselecting the property “Multiple Use”. When instantiating a single use BAdI you have to make sure that there exists only one active non-default implementation. Otherwise you get the respective exceptions at runtime. These exceptions can be caught, but this is not our strategy here. We want to avoid the exceptional situation right away.
Obviously, it would also make no sense, because we need exactly one result for our calculation and could not make any use of if the two BAdI implementations would be called one after another. This was just why we defined our BAdI as single use. What we need is a way to select among different BAdI implementations. And this is where the BAdI filter enters the game.
What we need is to change the BAdI definition, that is one we need to add a filter. You can define one or many filters for a BAdI. We will be modest and be content with one filter. Let us just define the filter in the BAdI definition, determine filter values for the respective BAdI implementation and use the filter in the instantiation of the BAdI handle. I hope in hindsight, you will understand what we have done and why.
You should keep in mind that what we do now when modifying our example does not take into account the differentiation between the BAdI provider and the implementer. In real life it is the BAdI provider who defines a BAdI with a filter or adds a filter to an existing BAdI. It is also part of this role to use the filter condition in order to select the respective BAdI implementation in the ABAP code. It is the implementer who determines the filter value(s) or an interval for one or many BAdI implementations. In the example we do all this ourselves for the sake of the example.
We start by adding a filter to our BAdI (role of BAdI provider) and to this we navigate to our enhancement spot and stay in the tab “Enh. Spot Element Definition” that shows a list of all the enhancement elements the spot. We switch to the change mode, mark our BAdI in the list, and click the Filter icon above. A dialog box opens and we fill out the relevant fields as shown below:
We confirm and get to the next screen. Obviously, the filter is now visible as a property below the BAdI. After activating the enhancement spot, we double click Implementation and navigate to the respective BAdI implementation by double-clicking the respective row in the table of BAdI implementations.
We switch to the change mode, click the triangle in front of the BAdI implementation in the tree and then double-click the filter icon below. In the next window we select the Combination icon, select “Country” as filter and confirm:
Double-clicking the row below “Combination 1” leads us to the next window:
As I have told you above, adding or changing a filter value of a BAdI implementation is part of the implementer’s role.
After we have activated the (simple) enhancement implementation, we navigate back to the spot. Apparently, the other implementation, that is the one for USA needs also the respective filter value. So we go to the respective (simple) enhancement implementation and change the BAdI implementation in an analogous manner. The respective filter value for this bcountry is ‘US’. I skip the description of how to do this, because what we do is identical to what we have done before when Bcreating the filter value for the BAdI implementation z_bdi_calc_vat_gb. We must take care that we do not forget to activate the (simple) enhancement implementations and to select the property “Implementation is active” for every BAdI implementation.
Now it is time to return to our program and adapt it to the modified BAdI (this is also the task of the BAdI provider). Running the syntax check shows you that you need to have a filter parameter in the GET BADI command if the BAdI you try to instantiate is defined with a filter. The requisite addition to the GET BADI command is FILTERS. It is after this keyword that you insert the name of the BAdI filter and a value the filter is compared to. Of course, we also have to take care that an appropriate value can be passed to the GET BADI routine:
REPORT Z_DEMO_ENH. PARAMETERS: ctry(2) type c. DATA: handle TYPE REF TO z_badi_calc_vat, sum TYPE p, vat TYPE p, percent TYPE p. sum = 50. GET BADI handle FILTERS Country = ctry. CALL BADI handle->get_vat EXPORTING im_amount = sum IMPORTING ex_amount_vat = vat ex_percent_vat = percent. WRITE: 'percentage:', percent, 'VAT:' ,vat.
If you pass “GB” to the parameter ctry, you will get a VAT rate of 16,5 percent. If the value of the parameter ctry is “US”, the VAT rate will be 4 percent. When the parameter ctry has any other value, we will still get a calculated value for the data field ‘percent’. And this is because we have defined a fallback class for our BAdI. The fallback class is not only selected if no BAdI implementation is available, but also if none of the existing BAdI implementations meets the filter conditions in the GET BADI command. Accordingly, we get a VAT rate of 20 percent. And this was the VAT rate we have implemented in the method get_vat in the fallback class.
And this is the end of this weblog. You have now learned how to create a BAdI implementation for a given BAdI. You know that a (simple) enhancement implementation is a container for BAdIs and moreover how and also why to assign it to a (simple) enhancement spot. That is you have understood the way the different elements of the enhancement framework interact when implementing a BAdI. And, in the second part; you have learned how to create a filter for a BAdI, how to determine filter values for BAdI implementations and what the appropriate syntax for calling a BAdI with a filter is like.