I’ve just finished another framework design for this scenario using SAP’s BAdI technology. You can find it here.

The premise

Hi guys and gals!

I’m pretty sure this has happened to pretty much everyone out there working in an international company. Sometimes a requirement comes along which is only supposed to be developed for a certain country. Or certain sales organization, certain whatever. It happens. The natural (and lazy) solution? The good old “IF” statement, correct? Now, if you’ve been around long enough you’ll know that, sooner or later, this will be a complete nightmare to maintain, with developers blocking one another, fighting over who is locking the include, fighting over who gets transported first, etc etc… so, what to do?

This blog post will propose a solution for a data transfer (or copy routine, or pretty much whatever) which I “designed”. Well, in all honesty, I didn’t really design it myself. Basically it is a “merge” of a pretty nice framework that was already in place somewhere I’ve worked before, and this blog post right here: Factory-Pattern in ABAP OO.

The idea

Ok so what’s the idea? Not very complicated actually. Let’s say that there will be requirements common throughout the entire system, the entire landscape of companies/users/whatever using the system. Let’s call these (and this is not an original idea) the “CORE” requirements. Next, will come maybe requirements for a specific country? Maybe a specific sales organization? I guess someone should decide how “granular” this encapsulation should be, the compromise being, if you’re too specific, you’ll have to configure a lot of table entries (or ifs) for the same requirement. Not specific enough and you’ll still have some fighting over who is locking what, but not as bad as before. Or you can start general and then repeat the process on a more specific level. I don’t know, that’s why I’m writing this blog post, let’s discuss 🙂

So, I picked up a pen and a piece of paper and tried to design something. I came up with this. An interface that the specific classes will implement. A factory that will return one object implementing this interface. A class that will execute the CORE requirement and will ask the factory for the specific requirement and execute it. I’m going to try and draw it based on other examples. Let’s see:

2014-02-07 11_37_52-Clipboard.png

Wow… this came out worse than I thought it would… You can still refer to the blog post I mentioned above. The drawings are prettier 🙂 Hopefully you’ll be able to get the full picture when we get to the implementation!

The implementation!

Ok so, we’re going to be looking at something pretty standard (I think). Data transfer from a sales order into a delivery. From the standard routine we have following information:

*       The following work areas are available:                       *
*                                                                     *
*        LIKP – Header of the delivery                                *
*       CVBAK – Header of the reference document                      *

So I thought… I should probably start with an interface for that. A very simple interface it turned out to be indeed:

Interface ZIF_VBAK_2_LIKP.png

With the following parameters:

Interface ZIF_VBAK_2_LIKP_2.png

I created a class for both the core requirements and the specific requirements, both implementing this interface… not that it would be important for the core class to implement it, but it’s nicer! Keep in mind that if you want to implement the interface in the core class, it will have to be instantiated, so for now I didn’t implement the interface in the core class!

Class ZCL_VBAK_2_LIKP_CORE Change.png

Class ZCL_VBAK_2_LIKP_4230 Display.png

Ok so now we should implement the factory! As you can see in the blog post I mentioned above, you can read some customizing table, and you can pretty much return any object you see fit, as long as it implements the interface we created in the beginning. I think that’s pretty cool. So here’s my factory:

2014-02-07 12_00_12-Clipboard.png

Now the static method that I will call from the data transfer routine!

Class ZCL_VBAK_2_LIKP Change.png

And that’s it! To test this I wrote a very small program. Oh, yes, I forgot to catch the exception, so if I enter a sales organization different than 4230 (I hope that’s not confidential information…) it will dump… keep this in mind ok? 🙂

PARAMETERS: p_vkorg TYPE vkorg.

BREAK-POINT.

SELECT SINGLE * FROM vbak
  INTO CORRESPONDING FIELDS OF ls_vbak
  WHERE vkorg = p_vkorg.

zcl_vbak_2_likp=>transfer_data(
  EXPORTING
    i_vbak = ls_vbak
  CHANGING
    c_likp = ls_likp ).

Let’s discuss!

So now the interesting part (for me). Let’s discuss! What would you have done differently? While pasting I thought… it would probably be really cool to have the factory return a table of implementations and execute them one by one, pretty much like the BAdI framework. Would this be easy to implement? How? Leave your comments below, let’s see if we can come up with an optimal solution 🙂

All my best,

Bruno

To report this post you need to login first.

20 Comments

You must be Logged on to comment or reply to a post.

  1. Matthew Billingham

    For you’re last point I’d create my own BADI in SE18 and use the existing framework!

    What’s nice about this is that you’ve hidden the complexity away. If a new company comes on line, you only need to write a class for that company with its logic.

    Another approach would have been to create a superclass with the core process in it. The factory method would be something like:

    data: instance type ref to thisclass.

    classname = thisclass=>derive_classname( company ).

    try.

      CREATE OBJECT instance TYPE (classname).

    catch cx…  ” Exception for class not existing

      CREATE OBJECT instance. ” Type will be be to thisclass – i.e. the superclass

    endtry.

    (0) 
    1. Bruno Esperança Post author

      Hi Matt!

      Indeed, I think the next logical step is to look into the BAdI framework and learn how to use it and abuse it. I’m not someone who likes to reinvent the wheel! 🙂

      Regarding your alternative approach, I did think of it, but it had some design flaws which I didn’t like. The main one would be that I didn’t want the subclass to be able to redefine the core process. I couldn’t find a way around it, so I didn’t set up an inheritance relationship between them. I saw somewhere that you should only set up inheritance when you can say that A is B. In this case, you can’t. A specific process is not a core process. So I’m not sure this would be a good approach.

      What do you think?

      Thanks!!

      Best,

      Bruno

      (0) 
      1. Matthew Billingham

        I think the way you’ve done it is better, when you have to have the superclass method running. You can set individual methods to FINAL so they can’t be overridden. It all depends on how much control you want to give future developers

        (0) 
        1. Bruno Esperança Post author

          Ahh that’s right!! I forgot about the final addition! So I might revise my approach! Because what I was trying to do initially was to have an interface with methods “process_core” and “process_specific”, one core class with the core method implementation and just a void specific method, then the specific (sub)classes would redefine the specific method.

          In the data transfer routine the factory would be called, which could return a specific object or the core object, didn’t matter, and it would call both processes one after the other.

          I don’t see big advantages from this, but I think it would be a bit more readable and elegant. So many different approaches to try, so little time 🙂

          Thanks!

          Best,

          Bruno

          (0) 
          1. Bruno Esperança Post author

            PS: Like you said in the other thread, if some “cowboy developers” come along they will do whatever they want, so what I’m trying to go for is a framework that will prevent mistakes from “distracted” developers, not someone who is trying to corrupt the system on purpose 😛

            (0) 
  2. Paul Hardy

    You asked for comments, so I would say this is very good.

    As Matthew said, to avoid the dump when you put in a new sales organisation , in the BADI framework you would create a default implementation.

    In the BADI fraemwork you could have a “filter” on sales organsisation.

    I imagine you are talking about the good ole MV45AFZZ form based user exists here.

    I have seen this implemented in a procedural way before, basically in each MV45AFZZ user exit rotuine you had a whole string of PERFORM xxxxxx_country IF FOUND calls one after the other. The first thing each routine did was check if the sales organisation in VBAK matched the one it wanted.

    That worked sort of OK, but it all fell down when each programmer defined their form routine with a different interface (signature), with just the data structures they needed. Then they would change the signature of the FORM in the subroutine pool but not the caller – oh dear – and I’m not making this up. Then anyone using VA01 in any country would be stuffed by a syntax error. in the live system. And then I would get the blame as the logic was “it cant be any of the permanent employee programmers, so it must be that bloody Australian consultant”. Do I sound bitter?

    Anyway, with a BADI you have a fixed interface, so that problem does not arise.

    I only make this digression as an example of where OO is clearly better than doing the same thing procedurally, lots of people keep asking for such examples.

    SAP had a half hearted attemtp to provide BADIS to replace all the FORM based user exits, but I don’t think there is anywhere near 100% coverage, and some of those BADIS are marked “SAP Only”.

    Cheersy Cheers

    Paul

    (0) 
    1. Bruno Esperança Post author

      Hi Paul!

      First of all, let me say that coming from you this means a lot to me. Highly appreciated, thank you for your kind comment.

      So you were the standard “scape goat” for anything that went wrong in that project huh? Sorry for that, I know the feeling, it has happened to me as well, not very nice to be “fingered” all the time 😛

      I was talking about a data transfer in VOFM, but yes this would obviously apply to MV45AFZZ or any other equivalent routine based user-exit! That was the whole point of going the distance to design this! It should be useable pretty much anywhere 🙂

      Now, I did think about the BAdI solution over the weekend, and I am going to implement and test it, even if just for educational purposes, but I do think there are some strong disadvantages with it. The main one being that, as you know, BAdI’s can be implemented freely and there’s no guarantee to in which order they are executed, meaning it would be really easy for a “distracted” programmer to do some damage, like reimplement a BAdI for a condition that was already implemented and overwrite some other requirement…

      What do you think?

      I’ll write a couple more blog posts, with the inheritance version and the BAdI version, and then we can discuss which one looks better and is more robust. Personally I’m leaning to the inheritance version, but I guess it will be easier to decide when we see how all of them look like.

      Thanks!!

      Best,

      Bruno

      (0) 
      1. Andrea Olivieri

        Hello Bruno,

        I’m sorry, unfortunately for some strange reason, after the migration, my blog is no longer editable by me, but by the moderator …

        KR

        Andrea

        P.S.

        @ Moderators: Any suggestions?

        (0) 
        1. Bruno Esperança Post author

          Hi again Andrea,

          Since I still can’t comment on your blog post, what do you think about my reply to Paul? The BAdI solution, in my opinion, seems to have the advantage of being very flexible, but at the same time, the disadvantage of being very flexible 😛 I think it is very easy to make some damage with this kind of implementation, whereas the solution I provided above has a more formal design, and things would be kept under control more easily.

          I say this because, like I mentioned above, a BAdI can be implemented (or reimplemented) several times, and I think it is easy for requirements to have conflicts, whereas in the solution above, there will be a core method with requirements that are system wide, and then ONE specific method will be called, and we ensure that only one specific method is called with this design.

          Let me know!

          Best,

          Bruno

          (0) 
          1. Bruno Esperança Post author

            I’m going to answer my own question here…

            So you can configure a BAdI to be single use, and use the filter option to choose the appropriate implementation. I thought the single use option meant that you could only implement the BAdI once, but this is not true after all! So the “problem” I mentioned above no longer applies.

            However, I still have one challenge left to solve with the BAdI solution… how to have a core process and a specific process? Maybe a hybrid solution would be the best way to go… first a method implementing the BAdI’s interface would be called in the user-exit to implement the core requirements, and after this call, a BAdI “hook” for the specific requirements. Could this be the “perfect” solution?

            Best,

            Bruno

            (0) 
            1. Paul Hardy

              Hello,

              About two hours ago I was thinking if the single use flag would solve your problem, but because you need the core method to always be called in addition, clearly just ticking the box on it’s own is not going to work.

              My first blog on the SCN was called “Back to the Future” and within this I was talking about programs for multi-national companies where some logic was always the same, and some logic varied by country.

              The verdict in the comments was unanimous – put BADI hooks at the places in the core program where individual countries would want extra functionality after the core logic was done. That seems to be exactly the solution you have come up with.

              After all, that’s what SAP do in their own programs now they have (sort of) moved away from form based user exits, and what;s good enough for them….

              Cheersy Cheers

              Paul

              (0) 
              1. Bruno Esperança Post author

                Hi Paul,

                That was the reply I was waiting for, thanks! 🙂

                I’m a little undecided on which solution would be the best, honestly. I think I’m still gonna go ahead and try to implement both the fully custom factory/interface based method and the BAdI method and see which one I like best. I’ll probably right a blog post for each and then have a “sum up” blog post with pros/cons for each, time permitting. I think there’s added value in this.

                Thanks again!

                Best,

                Bruno

                (0) 
        2. Bruno Esperança Post author

          Hi again Andrea,

          Another question I forgot to ask about your blog. You mention in it that the BAdI approach is not suitable for generated reports/routines like the ones in VOFM (precisely the one I’m mentioning in this example of mine). Could you expand on this? I cannot think of a reason why this might be a problem.

          Thanks!

          Best,

          Bruno

          (0) 
          1. Andrea Olivieri

            Hi Bruno,

            first of all I would say sorry for my late reply.

            In few words, the design phase of the BAdI interface with its methods and parameters is an important step in the definition of the framework discussed in this blog series.

            The main reason for my assertion (“…BAdI approach is not suitable for generated

            reports/routines…” ) is related exclusively to the signature of the VOFM routines where the formal parameters are completely missing.

            Moreover the VOFM routines are automatically generated and called dynamically from the application via customizing ; in my opinion here it is difficult to decide in one shot what are the correct parameters to specify in the method definition .

            In your example you probably started from a new VOFM routine copied from the standard one but let’s try to think of a new routine generated from scratch.

            We probably need put in trace or debug the application to determine what are the useful parameters to be transferred to the BAdI.

            Best,

            Andrea

            (0) 
            1. Bruno Esperança Post author

              Hi Andrea!

              No problem for the late reply 🙂

              Ok, I understand your point now! Regarding the parameters, something I found out while writing my last blog post, is that you can adjust BAdI’s interfaces with new parameters and it’s not really the end of the world, all you have to do is adjust the call, the rest of the implementations still work, so that is a really helpful feature from BAdIs. I hope I’ll be able to implement the design I proposed in a live system in the near future, and your blog post was a big help, so thanks again for it 🙂

              Best,

              Bruno

              (0) 

Leave a Reply