Implementing Rebate Voucher Functionality with BRF+ in a Classical ERP
In a recent development project, I successfully employed the Business Rules Framework BRFplus as the rules engine for voucher-based rebates in a retail scenario. In this blog, I describe my experiences and some implementation details. The bottomline is that I fully achieved my goal of implementing the rules in the business rules framework, keeping it separate from the ABAP code, which only calls the rules with the actual parameters. The rules are integrated into the classical SD pricing procedure which now functions as a façade. The processing of the rules is of highly satisfactory performance.
The Rules of the Game
This was the requirement: customers, when ordering goods in a store, could pass some rebate vouchers to the sales person, who, by scanning the vouchers, adds them as value items to the same SD customer order that contained his ordered items, resulting in a reduction of the order’s total amount.
A potpourri of rules had been invented to handle these vouchers, like the following:
- A voucher is valid for a certain assortment, for a certain label, or even for a certain article number, or a combination of these criteria.
- A voucher can result in a percentage reduction, or in an absolute reduction.
- Some vouchers require a minimum sales total for the ordered articles to which they refer.
- There are vouchers that can be applied only once. Another voucher kind counts multiple times, if the sales total for the ordered articles to which it refers is a multiple of its minimum amount.
- Yet another kind of vouchers can be applied multiple times, but not more than N times, where the number N is a property of the voucher.
- There may be restrictions on date and time: Some vouchers apply only during certain “Happy hours”, others only on certain days of the week.
When I read these rules, it was clear to me that they will soon be modified or extended by new rules. If the rules are deeply hidden in the ABAP code, it would be cumbersome to extend them. If the rules could somehow be written down in a more business-like manner, it would be easier to modify or extend them later on.
Ideally, a program implementing a certain development request should read “almost” like the rules as they are written in that request. It is an advantage if the business rules can expressed in a language which is as near as possible to natural language. Even when we decide to let only programmers change the code, it is of value to have it business-readable, as Martin Fowler points out in an interesting article on this topic:
If business people are able to look at the DSL code and understand it, then we can build a deep and rich communication channel between software development and the underlying domain. Since this is the Yawning Crevasse of Doom in software, DSLs have great value if they can help address it.
Indeed, business-readable code can be seen as a contract between programmer and business expert: A reference point for discussions on the actual system behaviour and on its possible future changes. From another viewpoint, it is a machine-readable specification. Basically, this is true for source code in general, as Jack W. Reeves pointed out in his now famous essay The Code is the Design (1992). But what turns a design document into a specification is that, in addition to being machine-readable, it is business-readable (which is not the case for the average source code of a project).
Since I am interested in this kind of questions, I thought this was a good topic to give the Business Rules Framework BRF+ a try.
We are on SAP_BASIS 702, so some features of BRF+ were missing. It came out that these gaps were insignificant and not essential for the rules at hand. There was only one OSS note to apply, concerning the internal handling of floated decimals. But even that note was not really essential, it only made the life easier.
Sales orders are created as normal SD orders, the vouchers representing a new kind of articles, resulting in value items of the sales order. Since we are in SD, prices are determined by a pricing procedure. Wouldn’t it be wiser to stick to the pricing procedure and use the “formula and conditions” exits (transaction VOFM) to implement all these rules?
Not necessarily! Bringing BRF+ into play brings the advantage that the rules determining the rebates are more transparent. If they are hidden in some ABAP form routines, the rules are difficult to find and will potentially become too complex.
Linking SD Pricing with BRF+
The idea was to use pricing formulae in the way of a façade for BRF+:
- Certain events of the sales order, e.g. if an item has been added or removed, if an ordered quantity or article number has been changed, and the like, trigger the BRF+ (re-)evaluation of the complete sales document.
- The results of the BRF+ computations are captured as attributes of an adapter class, which then effectively calls the BRF+ function.
- When the pricing procedure is propagated, certain formulae retrieve the amount for their condition line from the BRF+ adapter class attributes.
To illustrate this, consider the following formula which links the condition values with the results of BRF+ (it’s not precisely how it is implemented in our system, but it exposes the basic idea). This is the code for a custom formula 930, which is attached to the relevant conditions ZFRC and ZPRC in the SD pricing procedure:
form frm_kondi_wert_930. data: lo_rebates type ref to zcl_rebate_voucher. * Default values clear xkwert. * Instance contains the computed reductions lo_rebates = zcl_rebate_voucher=>get_instance( ). case xkomv-kschl. when 'ZFRC'. xkwert = lo_rebates->get_absolute_reductions( komp-kposn ). when 'ZPRC'. xkwert = lo_rebates->get_relative_reductions( komp-kposn ). endcase. endform. "FRM_KONDI_WERT_930
For this to work, it is essential that the single instance of zcl_rebate_voucher has processed the BRF+ rules before the pricing is called. This can be assured within the programming model of the SD sales order.
I am leaving aside for simplicity some peculiarities: For example, we are keeping track of the price bases of all sales items in an intermediate item pricing sum (VBAP-KZWI5 in our case). This is filled after the pricing of an individual item, but before the pricing of the complete document is processed (in subroutine PREISFINDUNG_GESAMT) later on. This way, the problem of reciprocal dependency (BRF+ needing SD pricing, and SD pricing needing BRF+ results) is circumvented.
To give you an impression of how fluent the rules read in the BRFplus environment, the following is a copy/paste from a central Loop Expression of the rebate ruleset. No tricks! The text is almost precisely what you read in the BRF+ detail view for that expression. In a sense, this is the source code of the new rules. If something has to be changed, it has to be done on this level.
with condition: Voucher Type is equal to 5000
Do the following operations…
- If Out of Date/Time is true, skip remaining rules and start next iteration
- Change Base amount after processing expression rebate price base
- If Base amount is less than Minimum amount, skip remaining rules and start next iteration
- Change FACTOR after processing expression Compute factor
- Perform actions/expressions and discard result after processing expression Apply rebates
In the BRFplus UI, the underlined terms are in fact hyperlinks, pointing to other BRFplus objects, mainly to expressions or data objects. The whole source code of the rebate rules can therefore be considered as a hypertext document, consisting of various expressions, rules and functions referring to each other. Each part of this web consists of some lines of text in natural (although formal) language.
I consider this a highly important shift in programming: On top of the host language ABAP, the business rules framework enables a new way of implementing business logic. The new meta level is fully devoted to the business domain, providing a directly readable specification of how the program works, in terms of the domain itself – whereas the host language is reserved for the technical and implementation details.
In my eyes, this is the value and the main benefit of BRFplus: The introduction of a new level of domain-specific programming, allowing a clearer separation of business concerns from “implementation details”.
It would be possible to abstain from that new level, using only ABAP to express all parts of the solution. It is then in the responsibility of the developer to write his code in different levels of abstractions, the highest abstraction on top exposing a pure domain-oriented view, the lower levels containing more “implementation-oriented” aspects of his code. This is possible by applying what Robert C. Martin calls the “step-down rule” in [Clean Code]:
We want the code to read like a top-down narrative. We want every function to be followed by those at the next level of abstraction so that we can read the program, descending one level of abstraction at a time as we read down the list of functions. I call this the Step-down Rule.
Using a framework like BRFplus, one gets this separation out of the box. The rules themselves are not written in ABAP but are composites of various BRFplus expressions. Implementation details may still be written on the ABAP level (although it is basically possible to write any code in BRFplus, there is no clear benefit of it.).
Interface and Dictionary
The element which connects BRFplus into the hosting ABAP code is a BRFplus function. In our case, the function has the name “Rabatte berechnen” – “Compute rebates” and has the following interface:
All the items of the sales document are divided into two subsets: The rebate items, and the retail items, the latter being the real sales items the customer wants to buy. Before the function is called, these two subsets are filled into two internal tables, enriched by some additional properties derived from master data and sales action documents.
I found it convenient to use the tables as changing parameters, containing input and output fields in one structure. This way, when looping over the tables, the results can be filled during the rule processing. Here is an example: The line structure of the retail item table. The boxed part is the output part: The different kinds of reductions and rebate points (“Cumulus points”) the item receives, and an internal table REDUKTION_COMPOSITION, documenting the contribution of the various rebate items to the total reduction of the retail item.
In BRFplus, the word “dictionary” gets more of its original meaning, it is thought as a glossary: The pool of data objects that are needed to describe the problem from the viewpoint of the business. In contrast, the ABAP dictionary is more a globally accessible type system – an essential counterpart of ABAP source code. Nowadays, nobody considers the ABAP dictionary as a “glossary of the business terms” in the first place: Although it was once intended for this purpose, “the DDIC” now firms as part of the “technical realm” – it is as development-specific as the source code itself.
Always when we have the freedom to invent names and texs for items in programming, we should choose them wisely. In his book “Clean Code”, Robert C. Martin recommends to change the names of functions, parameters and variables again and again during development and during refactoring sessions, until the code that calls these functions is as expressive and readable as possible.
This holds for the BRFplus dictionary as well. After having invented the names and texts, we should look how the rule code reads with these texts: Is it comprehensible? An improper naming distracts from the essential point, the intention of the code / the rule.
In my project, I created the data structures in the ABAP dictionary and used the option to import them into BRFplus. It would also be possible to define all the necessary types and data objects freely in BRFplus, with no reference to the ABAP dictionary. But at least the data involved in the BRFplus function signatures should be known in ABAP as well. This means a duplicate definition of semantically identical types: a necessary “Don’t Repeat Yourself” violation, which is handled best by using the ABAP dictionary binding mechanism (the alternative: to define the type in both worlds independent of each other, would be worse). When the structure is changed later in the ABAP dictionary, it can be actualized in BRFplus.
I wrote that I use my interface parameters as changing parameters. Strictly speaking, this is not true. I found out that the parameter passing to the BRFplus function always is by value: The parameters are moved into the function context before the function call; and after the call, the results have to be pulled out from the context. This means, there are some copy processes of data necessary. The method RABATTE_BERECHNEN of the adapter class is defined with changing parameters:
methods rabatte_berechnen importing is_timestamp type zcrc_timestamp optional changing ct_retail_item type zcrc_retail_item_tab ct_promo_item type zcrc_promo_item_tab raising zcx_error .
This is the implementation of this method: the BRFplus call, which shows the necessary data copying from and into the method’s changing parameters:
method rabatte_berechnen. data: lo_rabatte_berechnen type ref to if_fdt_function, lo_context type ref to if_fdt_context. * A voucher always has a range of articles to which it refers assert id zdev condition coupons_haben_artikelbezug( ct_promo_item ) eq abap_true. * A voucher with multiplicity 1 always has a minimum turnover value specified assert id zdev condition mindestumsatz_gepflegt( ct_promo_item ) eq abap_true. * Function processing only necessary if there are retail items and rebate items check : ct_retail_item is not initial, ct_promo_item is not initial. * Get handle to BRFplus function lo_rabatte_berechnen = get_function_rabatte_berechnen( ). lo_context = lo_rabatte_berechnen->get_process_context( ). * Pass parameters into function context lo_context->set_value( iv_name = 'RABATTPOSITIONEN' ia_value = ct_promo_item ). lo_context->set_value( iv_name = 'RETAIL_ITEMS' ia_value = ct_retail_item ). lo_context->set_value( iv_name = 'ZEITSTEMPEL' ia_value = is_timestamp ). * Execute function process_function( io_function = lo_rabatte_berechnen io_context = lo_context ). * Retrieve parameters from function context lo_context->get_value( exporting iv_name = 'RABATTPOSITIONEN' importing ea_value = ct_promo_item ). lo_context->get_value( exporting iv_name = 'RETAIL_ITEMS' importing ea_value = ct_retail_item ). endmethod.
I tried to keep the ingredients of the BRFplus function self-contained, avoiding interferences with the ABAP world. Here is the set of expressions that were necessary for the rebate voucher rules:
As you see, I used the following expression types:
- Boolean expressions,
- Function Calls, and
In particular, I didn’t use:
- Database accesses from within BRFplus. If database accesses were necessary, I did them before calling BRFplus and passed the relevant data using the function parameters.
- ABAP function and method calls from within BRFplus. ABAP function calls and method calls being necessary in an essential way, would question the use of BRFplus at all. Indeed, things like custom-defined, ABAP-implemented formulae should be the exception, not the rule when using BRFplus.
There might be cases in which these operations make sense. For example, ABAP method calls may be employed if there already exists a set of complex ABAP methods, and BRFplus is used only as a tool to combine (to “orchestrate”) these methods in a flow, according to certain rules. Or, the database access may be used for techniques similar to the condition table access of classical pricing.
But keeping the BRFplus isolated and publishing all its dependencies in the function interfaces, brings a couple of advantages. One of them is the ability to do unit tests.
Unit Tests and Debugging
Designing a function such that the result depends only on the actual parameter values with which it is called, makes it easily testable, for example with unit tests. I have assured the basic rebate voucher functionality with 42 unit tests, organized in 12 classes.
In each test class, the BRFplus function is called precisely once, in the class setup. (Here, I use a macro set to fill internal tables, reducing the code to the essential parts: Which components should be filled with which values, whereas the boilerplate ABAP code to populate structures and tables is hidden in a subroutine pool zut_au_forms.)
method class_setup. data: ls_promo_item type zcrc_promo_item. _insert_row_n_fields gt_retail_item 'posnr;matnr;bossnummer;reduktion;preisbasis;waehrung;promofaehig' : '10;M1;220112548789;;1000;CHF;1'. _set_struc_n_fields ls_promo_item 'posnr;matnr;typ;betrag;mindestumsatz;mehrfach' : '90010;RABATI;5000;10;;1'. _insert_row_n_fields ls_promo_item-sortiment : "insert the article range of this voucher 'aktnr;pos_type;id;excluded' '12345;1;2201;0'. insert ls_promo_item into table gt_promo_item. clear go_testee. rabatte_berechnen( ). endmethod. "class_setup
The tests themselves only check that the function produced the expected results – like the following:
method faktor. assert_equals( act = gs_promo_item-faktor exp = `0.01` msg = `Faktor has to be 0.01` ). endmethod. "faktor method composition. field-symbols: <ls_promo_comp> type zcrc_bonus_composition. read table gs_retail_item-reduktion_composition assigning <ls_promo_comp> index 1. assert_subrc( act = sy-subrc msg = 'Voucher must be part of the item's reductions' ). assert_equals( act = <ls_promo_comp>-promo_posnr exp = '90010' msg = 'Voucher item number needs to be passed into item composition table' ). assert_equals( act = <ls_promo_comp>-betrag exp = 10 msg = 'This voucher must give a 10 CHF reduction' ). endmethod. "composition
The unit tests can be repeated frequently during development, since they don’t consume much time. If one or more of them are broken at a certain point, it must be due to the last changes. A static analysis of these last changes usually helps understand the cause why they are broken. The time to fix a new bug is usually much shorter than with a trace or debugging session.
It should be mentioned that usually there is no necessity to debug the BRFplus logic, since there is a detailed trace which logs the result and all the details of each processed expression. I’ll come back to that in a moment.
But even when you decide to debug, the module tests are helpful. By placing a break-point at the function call which produced a test failure, and hitting “Module test”, the call can be repeated easily, again and again. The BRFplus rules are translated into ABAP code (code generation). But the good news is that the generated code is readable and the generation concept is readable and comprehensible – hence debuggable.
Understanding the Result
The backoffice workers – usually not the sales persons themselves – involved in the sales order management, sometimes want to know why a particular voucher is accepted or not accepted, and how it contributes to the order’s total reductions. For these cases, an analysis screen is helpful. I designed such a screen and made it available with a button in the GUI status, using a new function code in the screen sequence control (transaction VFBS).
If the user hits the new button, the following screen is displayed, showing all the relevant data for the rebate voucher processing – including the results, which are computed again in simulation mode when the screen is called.
By double-clicking on a retail item, a popup appears listing the contributions of the different voucher items to the total reduction of this item.
For the daily business, the informations on this screen contain all the necessary information. But in support, sometimes a more detailled trace will be necessary. For this reason, I made the so-called technical trace of BRFplus available in the above screen. It can be used directly in the BRF+ Web Dynpro application, as described in a blog by Phillip Parkinson but then the interface data would have to be entered manually, which is a time-consuming and error-prone business for our function: Two internal tables would have to be populated field by field, row by row – with data that are available in the order anyway. In order to populate the interface automatically with the current sales order data, I decided to call the trace in ABAP code. This is the method performing the trace call:
method process_function. go_context = io_context. call method io_function->process exporting io_context = go_context iv_trace_mode = if_fdt_constants=>gc_trace_mode_technical importing eo_trace = go_trace eo_result = go_result. endmethod.
The trace object eo_trace passed back from the function call, contains an XML representation of all the evaluations performed during the call. This trace is very detailed. The support employee, when analyzing a particular problem, can call it using the “XML” button in the detail screen shown above:
Due to its very detailed nature, I felt the desire to transform this XML into a more readable subset. There are certain building block elements of the XML structure like FUNCTION, EXPRESSION, RULESET, CONTEXT and CONTEXT_UPDATE which are combined in a hierarchical manner (reflecting the rules implementation, of course). I therefore designed an XSLT transformation ZCRC_TRACE which, applied to this XML file, produces HTML code in a more reduced manner, as the following screenshot shows. There are buttons for collapsing or expanding the subordinate informations on each level. Data objects can be inspected by clicking on the corresponding hyperlink, which shows them using the function module RS_COMPLEX_OBJECT_EDIT, which is also used for data maintenance and inspection in function module single test mode (SE37).
My experiences with this first BRFplus project are very positive. The implementation is stable and efficient. The vast majority of bugs and feature requests in the test phase referred not to BRFplus itself, but to the embedding of the rule processing into the SD sales order.
I consider BRFplus as a serious step towards the ideal of business-readable code. The introduction of a new level of programming, on top of ABAP as the host language, is a promising new route in development.
Also, I praise the co-operative philosophy of BRFplus: It doesn’t want to lead, to seize the whole development floor, but can co-exist with other implementations which at the same time are following a more traditional approach. There is no top-down strategy necessary (by the way, it was this top-down philosophy which broke SOA the neck). In the words of blogger Meinte Boersma: BRFplus, like all the model-driven-inspired development techniques, needs a kind of guerilla technique. Don’t make a big management issue about it – just employ it in all places where you find it makes sense. Every aspect of the system that gets implemented with BRFplus is your contribution to the better software world of tomorrow! 🙂
- Thomas Albrecht and Carsten Ziegler: BRFplus – Business Rule Management for ABAP applications. SAPpress 2010.
- Phillip Parkinson: Technical Trace in BRFplus. Blog, 09/19/2011.
- Meinte Boersma: In the trenches: MDSD as guerilla warfare. Blog, 10/01/2012.
- Martin Fowler: Domain-specific languages. Addison-Wesley 2010.
- Robert C. Martin: Clean Code. A handbook of Agile Software Craftmanship. Prentice Hall, 2008.
- Jack W. Reeves: Code as design. Three essays, The C++ Journal, 1992f.
Excellent blog, thanks for sharing Rüdiger.
Separating rules logic from the ABAP implementation makes for great design and runtime improvements as well as getting one step closer to giving the business access to their rules. You'll find the improvements in 731/703 and now DSM ease a lot of the common rules design and development tasks. The possible use cases for BRFplus really are endless.
I totally agree with what you were saying about making the code as readable as possible. Currently there are four ways I can think of off the top of my head to refer to a function of some sort within ABAP.
1. PERFORM calculate_discount.
2. CALL FUNCTION 'CALCULATE_DISCOUNT'
3. lo_sales_order->calculate_discount( ).
The last one is how BRF+ expects you to refer to a BRF+ function within your ABAP code. I notice in the code above you have replaced this with a constant, which is also what I do, and what I imagine most people who are actually using BRF+ are doing.
All the examples in the book and on the internet use a 32 digit hexadecimal number to refer to a BRF+ function.
If every single customer has to come up with a workaround then maybe that part of the product design needs to be revisited.
That is rather like the hexadecimal numbers all over transaction ST22 or the pages and pages of nonsense in a PI message. Useful information for a computer, not so much so for a human.
thanks for your feedback.
You probably are not argueing generally against the usage of GUIDs in software implementations?
Note that with the ABAP call of a BRFplus function we are on the ABAP level - one level below the BRFplus which I praised in my blog.
Of course, also on the ABAP level one should try to write the code as readable as possible.
I wouldn't call it a "workaround" if I define constants for literals in coding, but rather as good style or best practice. Although I am not too dogmatic with it: I don't think that every 'X' has to be replaced by the ugly and long ABAP_TRUE, and many string literals are not worth to be encapsulated in constants. The question whether to define a constant for a literal is: Is there a benefit in doing it?
For the BRFplus function ID's, I see the clear benefit of a where-used list of all places in the code where a certain function is called (or at least: used).
I am with you on items such as constants, particularly around where-used lists. However, I have used BRF+ in a number of implementations and do not like using the GUIDs as they are a pain to read - particularly for someone who has not authored the software.
In this instance, I now use the defined names instead of the GUIDs by referring to the IF_FDT_QUERY object.
A code snippet would look something like:
METHOD get_fdt_id. (public static method in my BRF utility class)
DATA: lt_ids TYPE if_fdt_types=>ts_object_id.
go_query->get_ids( EXPORTING iv_name = im_name
IMPORTING ets_object_id = lt_ids ).
IF lt_ids IS NOT INITIAL.
READ TABLE lt_ids INDEX 1 INTO rt_id.
The usage of this would then be things like:
go_function ?= cl_fdt_factory=>if_fdt_factory~get_instance( )->get_function( zcl_brfplus_utilities=>get_fdt_id( ‘MY_READABLE_NAME’ ) ).
zcl_brfplus_utilities=>get_fdt_id( ‘MY_READABLE_NAME’ )
. . .
I also use this to refer to other objects within BRF+ such as contexts etc.
If I happen to of created a custom configuration table for the process I am developing then I will often place the function name within it. This lifts the implementation one step higher by giving control to the business process owner.
I would definitely recommend checking out the query object to further improve readability of the calling application.
In principle such an approach is okay. However, you shouldb be aware that names in BRFplus must not be unique in general. Only for application and function names BRFplus checks that they don't exist twice (within an application).
For performance critical applications one needs to check whether an additional query call is acceptable.
Hi Pete, thanks for pointing to this alternative. However, when using named constants buried in some type pool (as members of a structured constant listing the set of functions), the GUIDS will very rarely hurt your sensitive eyes. In the code that uses the function, you will only see a constant with a carefully chosen name. Regards, Rüdiger
This is an amazing piece of work. I love it. Not only the final solution but also how you describe it and where you put the focus on: Readibility for business people. You understood which direction I wanted BRFplus/NW DSM to take. You did all this in NW 702. Expect more improvements in the current release (NW703). 🙂
1. DB Lookup or ABAP methods may need to be called to retrieve additional data that cannot be provided in the context. It is not always clear at the time of implementation of the BRFplus function call what data may be required for the rules.
2. Instead of doing instance calls (lo_function, lo_context) you may use the static method cl_fdt_function_process=>process. This is faster. In NW702 you may use report FDT_TEMPLATE_FUNCTION_PROCESS to generate the template code for the call. As of NW703 this feature is integrated into the menu of the function in the BRFplus workbench.
3. You can use lean trace in a way that the trace data is persisted. Then afterwards you can use the BRFplus workbench to check out the trace. It will show up like the simulation output. This works as of NW 702 SP 11 or so.
thank you for your valuable comments - and also for your support during implementation, e.g. in the SCN!
And thanks to you and the whole BRF+ team for having introduced such a groundbreaking development option!
@ DB lookup. Yes, there may be examples where a DB lookup during function execution makes more sense than providing all the data in advance as internal tables via the interface. So it's good to have this option. But I think, in a big majority of cases the specific data that are needed can be pre-selected in the ABAP code, before the rule call. In my example, this were some properties from article master and sales promotion documents (and the sales order data, of course, which are in memory anyway).
With best regards from Zürich,
Thanks for "FDT_TEMPLATE_FUNCTION_PROCESS", it is really helpful.
The product Help is really good but it looks like this information is left out.Or its probably a little trade secret 🙂
We are on NW703 but I don't see any option to generate the code template. Any suggestions please.
And it would be even more great, if this could be integrated to Patterns in SE80/SE38.
Make sure your user settings in the BRFplus workbench is in expert mode.
Thanks Philip, that solved it.Appreciate it.
Thanks Philip, that solved it.Appreciate it.
Excellent example and analysis of the "philisophy" of BRFplus. I'm looking forward to the first 731-release example 🙂
What a blog!
BRF+ is becoming one of the most important features in the ABAP programming and IMO should be adopted as a standard.
Thanks for sharing,
Excellent ! I hope I will get a chance to use BRF+ in one of my projects soon.
Thanks for the summary of your experience. I was hoping to use BRFplus myself on a project early this year and have been searching far and wide for a SD case study so your info was very timely.
That being said my requirements are slightly different and I was hoping you may be able to provide some thoughts/insight on my proposed design.
The client’s current / redundant & bespoke system uses CRM 5.0 + R3. I would like to replace this landscape with an SD + BRFplus Solution.
The customer bills for freight transportation on a monthly timeframe, however their contracts are very complex which makes pricing very complicated also. They have a vast mix of variable, overhead (fixed) and performance style charges so the current system has to gather a great deal of information in order to bill correctly.
Using SD documents (orders/shipments/deliveries + Custom tables) the customer collects all of the information which they need during the month and then a very customized CRM billing engine creates collective billing documents (1 per customer). While the same pricing conditions are used across customers – each contract has their own formula variations.
So with this in mind what I would like to do is structure the BRFplus design around condition types. Using this design it would be possible to use nested Decision Tables to allow customers to monitor and adapt their pricing. Formulas and DB Lookup Expressions would also be major players.
These were the main design characteristics of my proposal.
Thanks for taking the time to read this – any thoughts would be great.
your planned approach sounds very natural to me. The complication in my case was that for the rebate calculations the items' prices were required, so I had to run the BRFplus in a second pass, after the pricing itself. If intermediate sums or condition values are not required input for rules, then it is indeed much more natural to call BRFplus directly inside a VOFM formula.
Don't know precisely what you mean with "blended application", but it is actually one of the main goals of BRFplus to give things like decision tables into the hands of those who are really concerned with them - to the "business people", not the developers. The developers only implement the BRFplus call, and provide the parameters necessary as rule input and output.
Thanks for the confirmation! S&D is not my background so I was hoping for a second opinion!
By blended applications - I meant the following... have two BRF+ applications - one configuration and the other local (master data). So that I could let business people use and own simple expressions like decision tables but remove the more complex components (like DB lookups, case statements, formulas) which instead would be owned by the IT department. However by 'blended' I meant that all the BRF+ components (local and config) would be used in coordination within the bigger picture 'Pricing Engine'. Is this possible or does one implementation with BRF+ need to be all 'config' or all 'master data' ?
One other question for you: Did you find an appropriate metric to determine when a expression should be created in BRF+ or constructed in ABAP? What I mean is if I have a group of DB Lookups that are executed in high volume - is it more efficent to write a ABAP FM to carry out this function?
one BRFplus function A can call another BRFplus function B. So in your "blended" case, the developers may maintain function A which is called in the pricing formula, and which itself calls function B, containing rules maintained by the SD team.
As for DB lookup performance: I don't think there exists such a metric. The limit for BRFplus is only a syntactical one: Extremely complicated select statements cannot be implemented as BRFplus DB lookup.
You may also consider using NW DSM. With NW DSM you can model your rules on any server. You can stick to one application and give change authorization to specific objects only. Then you deploy from there to the target system for productive usage. This can all be done wihtout any code change and without any technical expertise.
Your question concerning ABAP versus BRFplus/NW DSM is one asked by many customers. As a rule of thumb I recommend to go with BRFplus/NW DSM when visibility is important or when changes are expected in the near future. You may also chose the rules approach when responsibilities for the logic are likely to change.