BRF+, OPD, and custom ABAP code
My requirement was to create a customer statement – easy enough. The next requirement was a little harder. It was to use the invoice OPD to determine e-mail or print. If it was e-mail then where to send it.
To start out, I’m on HANA On-Premise version 1709.
If you need help with setting up OPD, there are some very nice blogs out there:
New Output Management for Billing
My problem was really not the setup, it was how to add it to my program. Once I added it, I needed to understand how it worked. It took me longer than I thought it would. (By the way you can use this for BFR+ too) So it was something nice to learn.
So as usual I’m pressed for time. Let’s get started:
First I found the OPD, in BRF+. Transaction code BRF+. Keep in mind this was developed via Webdynpro so switching from client to client you’ll have to sign in on the html page.
Aha – my OPD applications
Next I opened up the billing document. There was a lot of great information here. But I needed to find something specific. So I opened up the function.
At this point you can run a simulation to test what will come up. Very nice. You can also do this via the OPD transaction. In the OPD transaction you can see the data that has been added. So now I’m switching to OPD as it is easier – at least for me – to work with.
I found the function I really wanted was channel. So next I started programming. Here are some snippets.
data: lo_factory type ref to if_fdt_factory, c_test_app_id type fdt_uuid, lo_query type ref to if_fdt_query, lt_ids type if_fdt_types=>ts_object_id, ls_ids type if_fdt_types=>id. types: lt_ids type sorted table of if_fdt_types=>id with unique default key . data: lo_fdt_query type ref to if_fdt_query. clear lt_ids. call method cl_fdt_query=>get_instance receiving ro_query = lo_fdt_query. * get application id call method lo_fdt_query->get_ids exporting iv_name = 'OPD_V3_BILLING_DOCUMENT' importing ets_object_id = lt_ids. read table lt_ids into ls_ids index 1. "get factory for application lo_factory = cl_fdt_factory=>get_instance( exporting iv_application_id = ls_ids ). * returning c_test_app_id = lo_factory ).
This just pulled in the id that I needed for the billing document. I retrieved the id for the function. Now those of you reading this – I skipped some functions. That was because they were 1 to 1 relationship. You’ll want to modify for your purpose. (Try/Catch would be nice to add as well.)
lo_query = lo_factory->get_query( iv_object_type = if_fdt_constants=>gc_object_type_function ). lo_query->get_ids( exporting iv_name = 'FUNC_CHANNEL' importing ets_object_id = lt_ids ). read table lt_ids into ls_ids index 1. if sy-subrc = 0. gv_channel_id = ls_ids. else. * Move to error table endif.
Next was the actual work. I’ve read not to use the following as it takes too long. I’ve also read this is the best way to do it. I wanted to avoid using the actual ids without the names.
lo_context->set_value( iv_name = 'OUTPUT_TYPE' ia_value = lv_form ). lo_context->set_value( iv_name = 'ROLE' ia_value = lv_role ). lo_context->set_value( iv_name = 'BUSINESS_PARTNER_ID' ia_value = lv_kunnr ). lv_function_id = gv_channel_id. try. data lt_abap_parbind type abap_parmbind_tab. data _v_timestamp2 type if_fdt_types=>timestamp. field-symbols <fs_any> type any. clear lr_data. get reference of lt_out into lr_data. **************************************************************************************************** * Trigger function processing **************************************************************************************************** assign lr_data->* to <fs_any>. call method cl_fdt_function_process=>process exporting iv_function_id = lv_function_id iv_timestamp = _v_timestamp2 io_context = lo_context importing ea_result = <fs_any> changing ct_name_value = lt_abap_parbind. lt_out = <fs_any>. read table lt_out into ls_out with key exclusive_ind = abap_true. if sy-subrc = 0 and ls_out-channel = 'EMAIL'. gv_print = abap_false. else. gv_print = abap_true. endif.
Now I had if the document should be e-mailed or printed. I found that our e-mail addresses were stored in transaction BP and not in the OPD. So I pulled the default e-mail address.
After you’ve read all this keep in mind. BRF+ will generate the code for you. I didn’t like all the hard coding.
You can also take all your coding out of it by generating a function module. Again this is in BRF+, but OPD is really part of BRF+. You want to be inside your function. Then click on tools. There will be an option to generate a function module. Again the hard coding made me hesitant to use it. I like more control.
More details in the following and you’ll notice some duplication from my post and this one. It really helped me a lot.
Pretty cool. Now I can think of a lot of uses for BRF+. Those custom tables that I have that determine something like highest dollar amount, those could easily be switched to BRF+.
That’s my quick example. Now a challenge – if you have a better way of doing this – please add it in the comments.
Nice one Michelle
Very nice!!! I actually like BRF+ a LOT but sadly, it just isn't used all that much at clients (at least from what I have seen). Not sure why (maybe "another thing" for the tech folks to have to learn?) because it is pretty dang simple to pick up for all the "power" it can give you.
Keep 'em coming! I like to see actual work in the trenches and not yet another marketing blog disguised as something I might like because it used technical terms in the title. hahaha
I'm laughing. A marketing blog this is not. But my next paragraph could be considered marketing.
I bought the BRFPlus Business Rule Management for ABAP Application book. I've gotten to look at it a little but I am still on "hot" projects. So I haven't had a chance to use this in any other project yet.
When I think about all those Z tables I've set up over the years to contain generic data, and then had to code if/then/case statements, I just shake my head. Using BRF+ for all of that makes so much sense. Of course I may change my mind but I doubt it.
Thank you for the nice comment!
Good Information ..Surely,I will try this.
Thank you! With the way OPD is set up BRF+ is a nice skill to have. It also can be used in many different places - I think. I'm going to keep trying to use it when it makes sense.
Hi folks, very helpfully
I am struggling looking a way to determine the Form Template Country in output control for PURCHASE ORDER, user needs to enter manually every time they need to send by EMAIL the PO even when the message is determined correctly but Template Country field remains in blank, any clue to determine this field automatically?
I believe you are looking for the t001 table. SPRO->Enterprise Structure->Definition->Financial Accounting->Edit, Copy, Delete, Check Company Code ->Edit Company Code Data. Enter the Country for the company code.
Thanks Michelle, but Company Code has defined Country field. As I told my problem is with field Template Country the one in the picture:
OK - I'll look a bit more. Country code is usually supplied on the SAP backend. Not in the output determination. It is used in output determination, as you noted, in finding the e-mail.
What version are you on? Are you in the cloud or on-premise?
I really appreciate your advice, our system is On-Premise, here are the version:
Hi, I appreciate any clue regarding this issue. Thanks in advance!
OK - I'll try. Country is defined in different places. So a lot of it depends on what you have configured.
You checked company.
The next thing I would do is check the master data.
How is your PO created? Check the transaction data.
Last but not least, try posting a question in the forums. Their are a lot more people there who might help you out. Remember when you post, it is not a problem with Output Determination. It is a problem with the purchase order country code. That way you can ask the correct question.
If all else fails, and it shouldn't, then you can make some custom changes to the code. (BADI, user exit, or enhancement point) But I would really discourage you from doing that until you've tried all other options.
Best of luck!
Hi Hector and Michelle, I am bumping into the same issue but then for sales order. Did you find the solution by any chance?
Looking forward hearing from you.
Did you try looking at note 2575996? It might help you out.
I eventually solved the issue with adding other role to receiver. So problem is solved, thank you! Your blog is really helpful.
Perfect! Now the next person knows.
First thanks for this write up.
Will the approach detailed here be useful for my below requirement ?
Please suggest on this.
Thanks Michelle for the detailed information on BRF+.
I wanted to know if is it possible to have editable Email body for the outputs which we setup using BRF+ so that a note is added by user. Currently user is using Medium 7 to send mails to multiple recipients and also adding a note to the email. Using BRF+ we can send mails to multiple recipients. But is it possible to add email body every time a mail is sent?
Excellent document. Appreciate the effort and sharing the document.
I have a question about how to enable "Messages" tab at a PO without hitting Save. Is it possible to do this without ABAP code?
Not to my knowledge. I know we used to use ME9F. But I believe that will only work for purchase orders defined the "old" way with NAST.
I have to change recipient email address in EMAIL channel output in PO BRF+. I implemented BADI BADI_MM_PO_OC_EMAIL method IF_BADI_MM_PO_OC_EMAIL~CHANGE_RECIPIENTS, but BADI is not triggered.
I'm wandering what am I missing? Any help is appreciated.
I also tested this BADI but it was not working with OPD.
I found another solutions :
1) Using your Callback Class -> method GET_DATA_FROM_ROLES
This solution is simple. But we can only add 1 TO email adress.
Please let me know if you found something else 😉
I think it depends on what you are doing. For me I had to add more than one e-mail to my output. To do this it required a enhancement to the program itself. It worked - not sure if it is a recommended approach.
To create multiple emails I used IF_APOC_OR_PARAM_DETERMINATION~DETERMINE_EMAIL_RECEIVER and set an enhancement point at the end of the method.
I checked the object type and the channel. Here's what it looked like.
Hi Michelle Crapo
I have 2 output
1- order confirmation
2- order confirmation change
i want second output should trigger when any changes made in sales order,