Skip to Content
Technical Articles
Author's profile photo Michelle Crapo

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:

Output Management via BRF+

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.
    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
      ro_query = lo_fdt_query.
* get application id

  call method lo_fdt_query->get_ids
      iv_name       = 'OPD_V3_BILLING_DOCUMENT'
      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 ).
      iv_name           = 'FUNC_CHANNEL'
      ets_object_id     = lt_ids ).
  read table lt_ids into ls_ids index 1.
  if sy-subrc = 0.
    gv_channel_id = ls_ids.
* Move to error table

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.


            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
                iv_function_id = lv_function_id
                iv_timestamp   = _v_timestamp2
                io_context     = lo_context
                ea_result      = <fs_any>
                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.
              gv_print = abap_true.

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.

BRF+ Basics

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.

My learnings in BRF+


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.

Assigned Tags

      You must be Logged on to comment or reply to a post.
      Author's profile photo Gokul Anantha
      Gokul Anantha

      Nice one Michelle

      Author's profile photo Michelle Crapo
      Michelle Crapo
      Blog Post Author

      Thank you!

      Author's profile photo Christopher Solomon
      Christopher Solomon

      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


      Author's profile photo Michelle Crapo
      Michelle Crapo
      Blog Post Author

      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!



      Author's profile photo Syambabu Allu
      Syambabu Allu

      Good Information ..Surely,I will try this.


      Author's profile photo Michelle Crapo
      Michelle Crapo
      Blog Post Author

      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.

      Author's profile photo Hector Manuel Guapo
      Hector Manuel Guapo

      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?


      Author's profile photo Michelle Crapo
      Michelle Crapo
      Blog Post Author

      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.

      Author's profile photo Hector Manuel Guapo
      Hector Manuel Guapo

      Thanks Michelle, but Company Code has defined Country field. As I told my problem is with field Template Country the one in the picture:


      Author's profile photo Michelle Crapo
      Michelle Crapo
      Blog Post Author

      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?

      Author's profile photo Hector Manuel Guapo
      Hector Manuel Guapo

      I really appreciate your advice, our system is On-Premise, here are the version:

      Author's profile photo Hector Manuel Guapo
      Hector Manuel Guapo

      Hi, I appreciate any clue regarding this issue. Thanks in advance!

      Author's profile photo Michelle Crapo
      Michelle Crapo
      Blog Post Author

      OK - I'll try.   Country is defined in different places.   So a lot of it depends on what you have configured.

      You checked company.

      • In configuration - I believe - you can check the plant/storage location combination.  In configuration check the sales organization.
      • Check the purchase organization in SPRO.


      The next thing I would do is check the master data.

      • check the vendor in transaction BP.  Display the financial Services role and check it.

      How is your PO created?  Check the transaction data.

      • If it's create from a sales order - take a look at the sales order.   What country was used?
      • If it's created from the purchase transaction without referencing a purchase requisition.   Then it should automatically be filled.   But take a look at the purchase order anyway.
      • If it's created from an info-record, take a look at the info-record.  Is country filled in?
      • Is there any custom code used?  Could it effect the country?

      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!



      Author's profile photo Lisa de Moor
      Lisa de Moor

      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.

      Best regards,


      Author's profile photo Michelle Crapo
      Michelle Crapo
      Blog Post Author

      Did you try looking at note 2575996?  It might help you out.

      Author's profile photo Lisa de Moor
      Lisa de Moor

      Hi Michelle,

      I eventually solved the issue with adding other role to receiver. So problem is solved, thank you! Your blog is really helpful.

      Author's profile photo Michelle Crapo
      Michelle Crapo
      Blog Post Author

      Perfect!  Now the next person knows.

      Author's profile photo Sijin Chandran
      Sijin Chandran

      Hi Michelle,

      First thanks for this write up.

      Will the approach detailed here be useful for my below requirement ?

      Please suggest on this.



      Author's profile photo Umesh Patil
      Umesh Patil

      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?

      Author's profile photo Johnson Li
      Johnson Li

      Hi Michelle,

      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?

      Thank you,

      Johnson Li

      Author's profile photo Michelle Crapo
      Michelle Crapo
      Blog Post Author

      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.

      Author's profile photo Johnson Li
      Johnson Li



      Thank you.


      Johnson Li

      Author's profile photo Helena Barshay
      Helena Barshay



      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.




      Author's profile photo David Scarlata
      David Scarlata


      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 😉



      Author's profile photo Michelle Crapo
      Michelle Crapo
      Blog Post Author

      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.

      IF is_or_item-appl_object_type = 'PURCHASE_CONTRACT' AND
             is_or_item-channel = 'EMAIL'.
       data(lv_zpc) = iv_appl_object_id(10).
      * Save default fields
        READ TABLE et_email_receiver INTO DATA(ls_zrec) INDEX 1.
        SELECT *
          INTO TABLE @DATA(lt_zemail)
            FROM zpurcon_intemail
               WHERE ebeln = @lv_zpc.
        IF sy-subrc = 0 AND
           lt_zemail IS NOT INITIAL.
          CLEAR et_email_receiver.
          LOOP AT lt_zemail INTO DATA(ls_zemail).
            CLEAR: ls_zrec-email_role, ls_zrec-email_uri.
            IF ls_zemail-zemail_or_cc = abap_true.  "Copy
              ls_zrec-email_role = 'CC'.
              ls_zrec-email_role = 'TO'.
            ls_zrec-email_uri = ls_zemail-zemail.
            APPEND ls_zrec TO et_email_receiver.
      Author's profile photo Avika Srivastava
      Avika Srivastava

      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,