Skip to Content
In my prior blogs:-
I talked about the historical background that caused me to want to re-write an application in a modern way, steps I have already taken to try and make custom programs more “portable” between SAP systems, and how I am going about building the new “modern” version of the example application, the latter being the subject of this and all subsequent blogs in this series, I thought I would need a lot more, but I am already fairly close to the best way to resolve the problem.
I am giving a tie-in speech on this subject at the SAP Australian User Group annual conference in Melbourne on the afternoon of August 23rd 2012. I danced around the room with joy this afternoon when my speech was mentioned on the email broadcast to all Australian SAP user Group members i.e.
“With over 70 extraordinary speakers, including 20 keynotes, covering 12+ industries, you will walk away with the latest and most innovative strategies covering all areas of your business from:
·        Best-Practice Data Warehouses using SAP BusinessObjects and Sybase IQ
·        5 Key Things to Consider when Implementing SAP into a Rapidly Changing Organisation
·        Back to the Future – Tips on Worldwide SAP Rollouts”
Well, that makes me a happy bunny, I will just have to wait and see if anyone actually turns up. Anyway, keeping to the format of my prior blogs, I will talk about my frantic rushing around trying to use all the new features in ECC 6.0, and then I will finally get round to mentioning the solution to my main problem i.e. getting the same program to work differently in different countries.
Error Message in a Bottle
The extended program check is a lot better than it was before, but the normal syntax check gives some strange results, examples as follows:-
/wp-content/uploads/2012/08/image001_126682.png
/wp-content/uploads/2012/08/image002_126715.png
                        
/wp-content/uploads/2012/08/image003_126714.png
A touch of Local Class
Now that I am playing with OO programming as opposed to procedural programming, I find myself using local classes a lot, especially for test classes. In ECC 6.0 you can see the method implementations using the SE80 display, which was not the case in 4.7.
This explains something that was puzzling me. On the discussion on the blogs about “ABAP in Eclipse” I bitched and moaned about SE80 not displaying things as well in SE80 as you could get in Eclipse and I was burned at the stake as a witch, with comments like “do you even use SAP?”. I presume the people saying that were using the current version of SAP, so they were looking at something totally different to me, which is why neither of us could understand the others point of view.
One gap seems to be that you cannot double click on a local method definition and have the system automatically create a template for the implementation, like you can when double clicking on PERFORM XYZ. I am sure SAP will remedy that in the future – if they are really serious about promoting OO programming it has to be as easy as procedural programming – but in the meantime I am writing a “pattern” to do this for me.
/wp-content/uploads/2012/08/image004_126716.png
/wp-content/uploads/2012/08/image005_126718.png
/wp-content/uploads/2012/08/image006_126719.png
/wp-content/uploads/2012/08/image007_126721.png
I am still working on this, but generally this looks like it is going to be a time saver. The breakthrough was discovering the statement SCAN ABAP-SOURCE. I am sure that has been there for a long time, but I only just discovered it. It lets me store all the class and method definitions and see if they have been implemented yet, and if not generate the missing skeleton.
/wp-content/uploads/2012/08/image008_126722.png
Shut That Door – New ALV Model
Moving on from my prior experiments, I had run into the brick wall that CL_SALV_TABLE could not be made editable. Today I found a function module that solved my problem. As we will see I am still not 100% satisfied, but it is a big step forward.
If I want to make the grid editable, I have to call the grid in full screen mode.
/wp-content/uploads/2012/08/image009_126723.png
This means I have to create my own STATUS with any extra buttons I want. I would have loved to do this programmatically but you cannot in full screen mode.
Now, in my user command routine:-
/wp-content/uploads/2012/08/image010_126724.png
/wp-content/uploads/2012/08/image011_126728.png
That makes the whole grid editable, the usual situation however is that I would just change one or more fields in the field catalogue to be editable.
/wp-content/uploads/2012/08/image012_126729.png
Anyway, the important thing is that this is supposed to be impossible, and it is not. I am probably being a fool mentioning this here though, as now SAP knows that this is possible they will close this back door.
If you can’t stand the Spead Sheet, get out of the kitchen – ABAP2XLST
I have finally got this working in regard to sending a spread sheet as an email in the background. An obvious use case is when an ALV report will time out if run in foreground, so we would add an option to run it in the background and email it to yourself as a spread sheet when it finishes. Often people download the spool file from a report and spend ages turning it into an excel file.
My boss suggested a few things that he thinks should be done to every spread sheet:
·         Freeze header
·         Enable Auto-Filter
·         Put spread sheet path and name in the header centre
·         Put Last Save Date in Footer left
·         Put Tab name in Footer centre
·         Put Page # of ## on Footer right
·         Set print mode to be landscape
None of that was difficult. Some of the above – freezing the headers and changing the printer orientation – I have done about ten billion times in my SAP career when downloading things from SAP into a spread sheet.
I am currently playing with several problems – the auto filter hiding all the rows sometimes and the automatic column width not working all that well. As this is an open source project and I am taking advantage of it, the proper thing to do is that once I have the answer to post it on the ABAP2XLS home page. I did this with the following example:-
I spent all morning debugging the EXCEL thing to see why all the columns were hidden, but only when you ran something in the background.
It transpires the function LT_VARIANT_LOAD was getting called and then an exception was raised…. And quite a serious one at that…..
/wp-content/uploads/2012/08/image013_126730.png
I have seen that sort of error handling loads of times.
What happens is that the program has no idea something has gone wrong, and so merrily goes on its way with all the data messed up…
I have made this observation on the sites home page and the author responded, so they are now aware of this.
It is my fault in that my program sent some silly data into the class, but then in the normal course of things you have to anticipate users entering silly data into SAP, so it is the same thing really.
Now, this is very much a work in progress, but my current way of enhancing a report which uses REUSE_ALV_LIST_DISPLAY or REUSE_ALV_GRID_DISPLAY (which 99.99% of our existing ALV reports use) is as follows, as an example:
image 99.jpg
/wp-content/uploads/2012/08/image100_126735.png
For the middle bit, example code for messing around with spread sheet objects can be found in the multiple reports that start with “ZDEMO_EXCEL”. The worksheet object you get is a spread sheet in the format of the way the report would look on the screen taking into account the field catalogue, sort catalogue, over-ridden by the variant if one is supplied.
The code exchange is a fascinating place, there are all sort of goodies to be explored. I was having a look at a program which analysed a program and turned it into a flowchart. I literally cannot switch off the italics in this paragraph. The “italics” ICON does nothing.

https://cw.sdn.sap.com/cw/groups/abap-flow-chart-maker

I am not sure how useful this is, but it got me thinking about using the same technique to analyse the program and bring up some sort of tree diagram showing the documentation for each subroutine / method / function call. That’s if there is any!
I was also trying – and failing – to get my head around the project which uses something called FITNESSE to do automated user acceptance tests in SAP. The very concept seems weird to me, and after reading the documentation I still don’t see how this is different than normal unit tests. If someone could explain it to me that would be great.
These Foolish Things Remind me of you, using Shared Memory
I have been playing with “shared memory objects” which are a new feature in ECC 6.0
The idea at first glance seems identical to buffering a table i.e. you are storing some data in a shared memory area where any programs running in the SAP system can read it at any time.
I suppose the idea is to store data which is complicated to read e.g. for a material you may get the data from MARA, MARC, MAKT and then get some characteristics. I gather that buffering does not work when there are inner joins involved.
Anyway, as an example, I have picked a table which stores data about surcharges we give to customers when the truck has to travel a long way. That has the distance as one of the primary keys, so buffering does not work.
So, first of all I have to create a class with the word “ROOT” at the end, which implements interface IF_SHM_BUILD_INSTANCE, and flag it as “shared memory enabled”.
/wp-content/uploads/2012/08/image014_126736.png
That class has a method for reading the database table and filling up the internal table, and another method for reading back data.
Then we use transaction SHMA to create a “shared memory area”.
/wp-content/uploads/2012/08/image015_126738.png
The important part is to set the “auto-start” to active,otherwise you get a dump if you try to read the data.
Then I create a test program as follows:-
REPORT  Y_SHARED_MEMORY_TEST.

PARAMETERS: p_werks TYPE werks_d.

START-OF-SELECTION.

DATA: lt_surcharges TYPE zty_sd_agg_distance_surcharge,
      ls_surcharges TYPE zssd_agg_distance_surcharge,
      lo_surcharges TYPE REF TO zcl_sd_agg_dis_surcharge.

TRY.
   lo_surcharges = zcl_sd_agg_dis_surcharge=>attach_for_read( ).
CATCH cx_shm_no_active_version.
   WAIT UP TO 1 SECONDS.
   lo_surcharges = zcl_sd_agg_dis_surcharge=>attach_for_read( ).
ENDTRY.

lt_surcharges = lo_surcharges->root->get_surcharges( id_plant = p_werks ).
lo_surcharges->detach( ).

LOOP AT lt_surcharges INTO ls_surcharges.
  WRITE:/ ls_surchargeszzplant,
          ls_surchargeszztruckrt,
          ls_surchargeszzdistf,
          ls_surchargeszzdistt,
          ls_surchargeszzaddrate.
ENDLOOP.

If I change a program to use this mechanism, then in effect the table is now buffered. That business about waiting for one second is because the first time someone tries to read the data, if the shared memory area does not yet exist then the “BUILD” method is called, which you have to write yourself, which is pretty stupid really, as the code has to be identical in every case, apart from the name of the class. Also, what if there is a system bottleneck, and the database read takes longer than one second? You would probably be better off running round in a loop a finite  number of times until you are sure the read is complete, or it is not going to happen.
METHOD if_shm_build_instance~build.
* Local Variables
  DATA: lo_handle    TYPE REF TO zcl_sd_agg_dis_surcharge,
        lo_root      TYPE REF TO zcl_sd_agg_dis_surcharge_root,
        lo_exception TYPE REF TO cx_root.

  “Open the shared memory area for write access
 
TRY
.
      lo_handle = zcl_sd_agg_dis_surcharge=>attach_for_write( ).
    CATCH cx_shm_error INTO lo_exception.
      RAISE EXCEPTION TYPE cx_shm_build_failed
        EXPORTING
          previous = lo_exception.
  ENDTRY.

  “Read the database and fill up the internal table
  TRY.
      CREATE OBJECT lo_root AREA HANDLE lo_handle.
      lo_handle->set_root( lo_root ).
      lo_root->set_surcharges( ).
      lo_handle->detach_commit( ).
    CATCH cx_shm_error INTO lo_exception.
      RAISE EXCEPTION TYPE cx_shm_build_failed
        EXPORTING
          previous = lo_exception.
  ENDTRY.

  IF invocation_mode = cl_shm_area=>invocation_mode_auto_build.
    CALL FUNCTION ‘DB_COMMIT’.
  ENDIF.

ENDMETHOD.

Just as a second part of this, I needed to add an “event” to the SM30 table maintenance for this Z table, so that after the user has changed the data the shared object is refreshed with data from the database.
/wp-content/uploads/2012/08/image016_126739.jpg
A change is as good as a test.
I would also like to say how useful I am finding the Unit Test framework in ABAP. I have a program that tries to match up customer open items, and I split up each rule into its own subroutine, and then created a corresponding test class for that subroutine. I know I should be using methods, but this program was written ages ago, and turning it into an OO version of itself is not really worth the effort.
Anyway, I put some example data in, and more often than not I find some glaring errors which I never would have found using the test data in the development system. Moreover, when the business analysts starts testing, he finds errors in the QA system, which has data which is a mirror of production, and I then update my unit tests, or add new ones, using the “real” data, see that the test fails, fix  my program, the test then passes, all is good, and I can be sure I have not broken anything else, as all the other tests still pass.
Anyway, after all that, let us get back to the subject at hand. The key element of what I am trying to do is to have core programs that react differently per country. The consensus of people responding to me was to use the enhancement framework to achieve this. As far as I can see this does not conflict with my basic idea of using OO subclasses, the only difference is, instead of using a big CASE statement with hard coded class names, or some naming convention, I can use the built in filter mechanism. It turns out you can have more than one filter value, which is good, I was worried about that.
In the following example, I am going right over the top, and using a sledgehammer to crack a nut, I am not saying this is a good, or even sensible, way to solve a problem; it is just an example for me to get my head round how this works. I am also still struggling with how to name the various objects.
Anyway, the example is as follows. My application model wants to send out a list of user commands that it can react to. The controller passes this to the view, which then decides how to display these buttons. Eventually the controller passes back which command the user has chosen.
Now, every country will have a slightly different set of buttons. It felt right to have the method that builds up the list of user commands to be a static method, but that would be no good, as then you could not redefine this method for each country. So, I went through the tutorial in the blogs by Thomas Weiss, to see if I could use a BADI to redefine a static method.
So, I create an interface for my application models, and give it an attribute which is a table of user commands, and a static method to fill that table. I then create a base class that uses the interface.
METHOD zif_bc_model~fill_user_commands.
*——————————————————————–*
* Each application model will have it’s own set of user commands
* the class name of the model is passed in, and then a class
* specific BADI does the different processing
*——————————————————————–*
* Local Variables
  DATA: lo_handle TYPE REF TO z_bd_fill_user_commands.

  TRY.
      GET BADI lo_handle
        FILTERS
          class = id_class.

      CALL BADI lo_handle->fill_user_commands
        RECEIVING
          et_user_commands = mt_user_commands.

    CATCH cx_badi_not_implemented.
      RETURN.
  ENDTRY.

ENDMETHOD.

In the above example, I am passing in a class name. Really I would be passing in a country, or even a sales organisation, as we have two totally different product lines operating in one country which behave in subtly different ways.
It is pointless to repeat every single step from the blog I used – you can read it yourself – but I went through it and created all the needed objects.
/wp-content/uploads/2012/08/image017_126742.png
It works fine. It seems incredibly complicated, with many objects to be created, but it works and that is the important matter. As I said, I have picked an example where you could do the same thing a lot simpler most likely, but that was not the point of the exercise.
Break Off
That’s it for now, once I have done my speech, and the presentation will be in the public domain anyway – it will be on the SAP Australian user group web site, alongside an audio recording – I will go through the contents in my next blog in as much detail as I can.
Then it will most likely be time to find another subject to waffle on about….
Cheersy Cheers
Paul
Final blogs on the subject at hand:-
Other OO Blogs:-
To report this post you need to login first.

10 Comments

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

    1. Rüdiger Plantiko

      Hi Paul, hi Jeroen,

      I am the author of that wiki topic. In the meantime I have already implied the code as a custom pattern in the ABAP editor. I will extend my wiki as soon as the SCN support team has granted me the rights to edit my own wiki topic (I have no edit button for it right now… 😯 ).

      Regards,

      Rüdiger

      (0) 
  1. Kenneth Moore

    Hey Paul,

    Great blog.  Question about using the ALV EDIT trick.  Do you know how to get the changed records after editing?  Editing the ALV records doesn’t seem to modify the table behind the scenes.

    (0) 
    1. Paul Hardy Post author

      There is a secret trick to this. You call a method of the ALV grid which appears to do nothing, but in fact updates your internal table. To demonstrate this, I modify my example “on user command” by calling the following:-

      CALL METHOD lo_alv->check_changed_data

        IMPORT e_valid = lf_valid.

      LF_VALID should get a value of X, and the internal table in memory will now match what is on the screen, so calling REFRESH_TABLE_DISPLAY is in order.

      It would still be lovely to be able to be able to add buttons to the status programatically whilst doing this, but sadly you can’t have everything. On “idea place” their was an idea for SAP to make the CL_SALV_TABLE editable as a standard option but some senior ABAP person from SAP went purple with rage and told us all to get lost.

      I have created a view class to wrap CL_SALV_TABLE so i don’t have to keep declaring all the helper classes ecah time e.g. CL_SALV_COLUMN_TABLE etc and can have a SET_COLUM_ATTRIBUTES method with lots of optional parameters to set the widths, or hide columns, or change the texts, say it is a subtotal etc

      That simplifies the calling code as it’s not always the same helper object you change e.g. to hide a column you use the column object but to say it is a subtotal you use the aggregation object.

      Just for fun, I created an interface for setting column and sort attributes, the list heading etc, so I could swap between REUSE_ALV_GRID, CL_GUI_ALV_GRID, CL_SALV_TABLE and ABAP2XLS, with the same code to set up the column attributes even though each of those classes uses very different techniques to change the column attributes.

      Cheersy Cheers

      Paul

      (0) 
    1. Paul Hardy Post author

      Greetings,

      In standard SAP you cannot do fancy things like freezing the panes whilst downloading a spreadsheet.

      What you need to do is go to the “code exchange” section of SCN, and join the ABAP2XLS project. You can then download the software for nothing, and you also get about 40 example programs of how to use the various features.

      The examples are not actually that useful, but they point you in the correct direction and anyway it’s a good thing that you have to apply a bit of brainpower yourself to get this working.

      In esseance you create an EXCEL object, and thenyou can perform operations upon it prior to download e.g. LO_WORKSHEET->FREEZE_PANES( IP_NUM_COLUMN = 1, IP_NUM_ROWS = 3).

      Cheersy Cheers

      Paul

      (0) 

Leave a Reply