1 6 7 8 9 10 67 Previous Next

ABAP Development

1,004 Posts

I was always wondering why we cannot create GUI STATUS directly from the code as it can be done in other programming languages ( VB or C# ). I was trying to find any solution for that but every source was saying that this is not possible. But it's only partially true. It's true we cannot dynamically create new function codes but as we can change icons and text of existing function codes that uses dynamic text. Using this technique we can prepare an GUI STATUS with all codes using dynamic texts and then update it in the runtime.

 

Ok, but if you like to do it then you wouldn't like to do it each time for each program as this makes no sense. But as you can call GUI STATUS of other program then you can create it only once and the reuse when needed. But still updating all icons, keeping in mind program name which stores GUI status would not be so handy, that's why in my blog I've posted a way to do it in nice and really reusable way.

 

In the example below you can see than I use static methods of a class zca_ab_dynamic_gui to add new buttons, show gui statu or title bar. Thanks to its class-data we can pass dynamic text to a program which keeps GUI STATUS.

 

2015-07-13_14h58_23.png

 

Result of this program will be:

dynamic_gui_status4.png

 

Seems nice right? But you have to remember also about the disadvantages of this solution:

  • you cannot change function code name
  • reading of PAI module will not be as clear as if you would put proper names to function code
  • separator does not look the same as in standard GUI

 

Advantage of using it:

  • you don't have to create GUI STATUS in SE41 each time, especially for small programs/reports
  • you can create / update everything using ABAP

 

 

Full code of class and explanation you can find here ABAP Blog - Dynamic GUI STATUS & TITLE with ABAP code.

Subject Matter.

The situation is familiar for any SAP consultant—transport queries are transferred to the target system with errors caused by violations of integrity of the transferred object. For example, a transferred program uses new tables (or new versions of older tables) but the target system does not “know” them, as these created/modified tables were not transferred and are absent from the query transferred with the program.

Such situations are fraught with problems: not only headaches and delays, but also more present the possibility of strategically important risks and losses.

Such risks are especially noticeable when the authors of these changes (programmers employed by the Contractor) are unable, that is to say, do not have the authority to independently transfer changes into the Customer’s target system. As a result of existing regulations, they are forced to contact the Customer’s basic consultants about these questions. Thus, a strict system of control of transferring changes is put into effect.

This situation is fraught with problems related to the loss of time and money (extension of deadlines and, as a result, terms of payment for completed work), but also can put a company’s reputation at risk. For example, unfriendly employees of the Customer have a reason to call into question the level of qualification of the Contractor’s programmers—they’re so stupid that they cannot even complete a simple transfer query! Yet the terms of the contract promised only highly qualified programmers.

As anyone familiar with the situation knows that even the most professional and experienced programmer is defenceless against such errors. There is always a risk—as a result of haste or carelessness, or because of more objective reasons e.g. the changes were made a long time ago, or due to complex interdependencies. Standard SAP tools do not offer sufficient protection.

All of this can eventually lead to real financial losses. Thus, the subject matter of this article goes beyond the scope of programmers.

However, by approaching the issue in a slightly more creative manner, it can be feasible to create tools that can help manage these risks.

The general concept of realization.

In order to analyse the integrity of queries, we must perform the following steps:

  1. 1.)    Select all objects contained in the transfer query and its subqueries.
  2. 2.)    Chain select all functions that are used directly or indirectly within this query and its subqueries. The topic of mutual use of functions is too vast to cover in this article. So we will limit ourselves to questions related to the use of programming functions, i.e. ABAP-code functions: АВАР-reports, АВАР-includes, classes. We will not analyse the use of data elements in table fields, domains in data elements, etc. Likewise we will also limit the types of functions used: only АВАР-includes and global types: glossary entries (database tables, structures, types of tables, pools and their components, data elements), АВАР-classes (with their components). Of course this is a limitation, but when you implement your tool and begin using it, you will see that even this limited variant “closes” 80-90% of problems. And even if this tool’s possibilities don’t cover your needs, with little effort will be able to easily build up a circle of analysed dependencies in its “image and likeness”.
  3. 3.)   For those functions used that are not present in the transport query, we should a remote version comparison between systems (we will test for differences. Of course, we will compare the source system and the target system.
  4. 4.)    If the function differs between systems and is absent in the transport query, we should signal the problem.

Implementation.

 

The analyser is carried out like a regular executed ABAP-report.

При For the conclusion of the analysis we will not use sophisticated UI elements like ALV. For our purposes, a conventional listing conclusion is sufficient.

I will deliberately be using the old-fashioned ABAP-code style (using HEADER-LINE and so on) in the interest of brevity and clarity (and in order to remove inessential structures necessary to maintain ABAP-code in a modern style). For unassisted implementation, I recommend following SAP’s recommendations related to the outdated constructions.

  1. The only input parameters we need are the query number and the name of SAP system whose compatibility we want to check. One could take the name of the target SAP system from the attributes of the query, were it not for the usual the three-levelled SAP system landscape (“Development”à”Test”à”Product”). Often it is necessary to determine the integrity of the query at all transfer stages.

PARAMETERS: p_trkorr TYPE e070-trkorr    OBLIGATORY MEMORY ID trkorr " What we transfer

          , p_system TYPE e070-tarsystem OBLIGATORY MEMORY ID tarsys " Where we transfer it

          .

Chain select all functions, contained in the transferred query and its subqueries.

The transport query’s header and the hierarchy of its subqueries are kept in table E070. The number of the query/task is in field E070-TRKORR. The number of the higher query for the task (subquery) is in field E071-STRKORR.

Objects contained in the queries/tasks are kept in table E071.

Objects can be directly linked either to the “main” query or to the subqueries (tasks).

*We will form RANGES from the number of the query and will receive a list of all objects contained in the query, in the field E071- TRKORR.

 

DATA: ltr_korr TYPE RANGE OF e070-trkorr WITH HEADER LINE,

      BEGIN OF ltd_objk OCCURS 0,

        object TYPE e071-object,

        obj_name TYPE e071-obj_name,

      END OF ltd_objk.

 

ltr_korr-sign = 'I' .

ltr_korr-option = 'EQ' .

 

SELECT trkorr INTO ltr_korr-low

  FROM e070

WHERE trkorr EQ p_trkorr

    OR strkorr EQ p_trkorr .

  APPEND ltr_korr .

ENDSELECT .

 

SELECT DISTINCT

        object

        obj_name INTO TABLE ltd_objk

  FROM e071

WHERE

       trkorr IN ltr_korr

   AND pgmid    IN ('LIMU', 'R3TR')

ORDER BY OBJECT .

In Table LTD_OBJK we now have a list of all objects included in the query and its subqueries.

In this article we are limiting ourselves to the following object types:

  1. 1.) АВАР-programs;
  2. 2.) АВАР-code includes;
  3. 3.)    Structures of global glossaries: database tables, structures, types of tables and data elements, as well as their components.
  4. 4.)    Global АВАР-classes (built in SE24), and their methods;

Field value OBJECT

Object Type

PROG

АВАР-programs and АВАР-includes

TABL

Definition of tables and structures of data glossaries

TYPE

Pools (groups) of types

FUGR

Groups of functions

CLAS

АВАР-class

METH

Separate method of АВАР-class

Chain select all objects directly or indirectly used as objects within the query or its subqueries.

Insofar as we are limiting ourselves in this article only to objects used as ABAP programs (and are not considering other uses, for example, of data elements in tables), we need the data sources from which we can receive information about the use of objects as ABAP programs.

SAP offers a good tool for this task: “Usage journal.”

Data on the use of objects as programs are stores in *CROSS* tables.

Table Name

Purpose

WBCROSSI

Use of АВАР includes in programs.

WBCROSSGT

Use of global types in programs, including use of components of tables and structures. Including the use of ABAP classes, their fields and methods.

CROSS

Use of functional modules in programs. In this table, we will also find information about the use of pools (groups) of types. In addition, this table contains data about the use of various other objects types, e.g. messages, but we will not consider them in this article.

WBCROSSGT is the most interesting and varied of these tables.

In SAP, the concept of “global type” is quite broad: glossary objects, tables, structures, data elements and their components. АВАР classes are also global types.

Every line of АВАР code with access to a global type or its component is given in a line of WBCROSSGT.

АВАР Code

OTYPE

WBCROSSGT- NAME

IF sy-subrc = 0 .

SY

SY\DA:SUBRC

DATA:

BEGIN OF ltd_objk OCCURS 0,

        object TYPE e071-object,

        obj_name TYPE e071-obj_name,

      END OF ltd_objk.

TY

E071\TY:OBJECT

E071\TY:OBJ_NAME

CALL METHOD o_grid-> set_table_for_first_display

ME

CL_GUI_ALV_GRID\ME:SET_TABLE_FOR_FIRST_DISPLAY

 

The field WBCROSSGT- INCLUDE contains the names of includes and programs, which refer to the global type or its component. Please note: it is not the name of the main program that is prescribed, but rather the name of the direct piece of the referring ABAP code.

 

Thus we can get a list of all global types involved directly or indirectly in the work of the ABAP programs, if we (beforehand) make a list of all includes involved in the program’s operations.

We simultaneously receive information about the usage of functional modules.

 

*We perform a search of all includes involved in program operations.

  DATA: BEGIN OF ltb_crossi OCCURS 0,

          name TYPE wbcrossi-include,

        END OF ltb_crossi,

        ltd_wbcrossi TYPE TABLE OF wbcrossi WITH HEADER LINE .

 

  LOOP AT ltd_objk .

    ltb_crossi-name = ltd_objk-obj_name .

    COLLECT ltb_crossi .

  ENDLOOP .

  SELECT DISTINCT * INTO TABLE ltd_wbcrossi

    FROM wbcrossi

         FOR ALL ENTRIES IN ltb_crossi

   WHERE

         include EQ ltb_crossi-name

      OR master EQ ltb_crossi-name .

 

* For all includes located, we perform a search of all global types used in these includes.

  DATA: ltd_wbcrossgt TYPE TABLE OF wbcrossi WITH HEADER LINE .

  SELECT * INTO TABLE ltd_wbcrossgt

    FROM wbcrossgt

                   FOR ALL ENTRIES IN ltd_wbcrossi

   WHERE include EQ ltd_wbcrossi-include .

* For all includes located we performa search of all functional modules involved.

  DATA: ltd_cross TYPE TABLE OF cross WITH HEADER LINE .

  SELECT * INTO TABLE ltd_cross

    FROM cross

                   FOR ALL ENTRIES IN ltd_wbcrossi

   WHERE include EQ ltd_wbcrossi-include .

Intermediate Results

We have received lists of the following:

  1. 1.)    All objects included in the analysed query and its subqueries (tasks);
  2. 2.)    Objects connected to the objects being developed.

 

Next, we must be guided by the following logic:

  1. 1.)    If the object is located in that same query, all is well—its current version will be transferred with the objects using it;
  2. 2.)    If the object is not found in that query, we must check if it should be, so that the transfer query goes smoothly. It’s obvious that if the given object’s version is identical in both systems, it won’t be necessary to transfer it.
  3. 3.)    If the object used is absent in the query and its versions differ between systems, we must warn about the presence of potential problems in the transfer of such a query. It is necessary to either include such objects in the query or transfer them into the target system in advance.

Comparing object version between systems.

 

There is a standard way to accomplish this task in SAP: the version control system with remote inter-system version comparison.

It is possible to check for differences between versions by calling FM.

SVRS_CHECK_VERSION_REMOTE_INT

 

Completion of parameters:

Parameter

Description

E071_ENTRY

The parameter with the structure of table E071, but only fiels  PGMI, OBJE, OBJ_NAME have real meaning. We can get the field values by making a selection from E071 by object name (we complete these fields in any query in which it is present.

DESTINATION

The name of the RFC connection for communication with a remote system. This RFC connection is created automatically when you configure the transport system. His name is composed according to the rule

‘TMSADM@’ + SystemName + ‘.’ + DomainTransferName .

The transfer domain name can be found by the name of the target system using the FM call system

TMS_CI_GET_SYSTEMLIST

Or one can use use FM call SVRS_GET_RFC_DESTINATION to receive the completed RFC address.

Result

If no differences are found, FM will work without error.

If differences are found, EXCEPTION will appear

NOT_EQUAL  – if versions are not identical

NO_VERSION – if the object has not yet transferred into the target system.

 

However, this FM has a strange feature: in theory, the message EXCEPTION NO_VERSION will appear if the object is absent from the target system. Nonethless, it will work without error, as if the object was present in the target system and no differences had been found.

 

Therefore, before calling SVRS_CHECK_VERSION_REMOTE_INT we will first call FM FIND_OBJECT_40, which will check for the presence of the existence of the object in the target system:

 

CALL FUNCTION 'FIND_OBJECT_40'

EXPORTING

   DESTINATION                 = TMSADM@’ + SystemName + ‘.’ + DomainTransferName

   OBJNAME                     = Technical name of the objext

   OBJTYPE                     = Type of object from field E071-OBJECT (FUNC, METH, etc.)

IMPORTING

   OBJECT_NOT_FOUND            = Flag: object not found in target system

 

Afterward, if OBJECT_NOT_FOUND is in fact empty, we will call SVRS_CHECK_VERSION_REMOTE_INT.


Victor Amosoff, ABAPFactory.com

We know that it is quite convenient to use transaction code SAT to trace traditional dialog application. However the application to be traced is not dialog application, for example, it is Webdynpro or Fiori application, the steps are not so straightforward. This blog Next Generation ABAP Runtime Analysis (SAT) – How to analyze program flow introduces the step how to trace such application with the help of "In Parallel Session" below.


clipboard1.png

This blog introduces a simple altnernative to trace a web application, written based on SAP_BASIS 7.40.

 

In my Fiori application I use an odata service to return the share calendar of given colleague. The odata service is launched by Chrome extension, postman. I need to do performance trace on it.

clipboard2.png

Step1: since I know the entry point of this odata service implementation is located in line 12, I set breakpoint there and click send button in postman, breakpoint is triggered. Click "New Tool" icon:

clipboard3.png

Step2: Special Tools, launch Trace( SE30/ST05 ):

clipboard4.png

double click the icon below:

clipboard5.png

Now the status turns green, which means the ABAP trace is switched on.

clipboard6.png

Step3: set another breakpoint at the end of the traced code, and click F7 to reach there. double click the icon in column "On/off" again, and now status turns red - trace is deactivated. And you should see an icon under column "TraceFile" which means the trace file is generated.

clipboard7.png

Step4: go back to SAT and you could find the generated trace file. Double click it, and the left steps are the same as you trace the dialog application.


clipboard8.png


clipboard9.png

As you see in this blog, you can also use this way to launch ST05 trace whenever you like during your debugging. The only limitation is, as could be found from the SAT file description, "Trace started with kernel default values", it is not possible to use your own customized trace variant to do the trace. If you do need some features which are only available by customizing trace variant, you have to use the steps mentioned in the blog.


Hi All,

 

In this blog i would like to explain briefly on the different text types that are used in smarforms. We all use text element node to display data via smartforms. Have you ever seen the different options avaialable while creating the text elements in smartforms? Do you know the purpose of each?

 

Well in this blog i would like explain the different text types and make you familiar on this. Hope this gives a very good idea to the beginners and others who seek knowledge on the same.

 

Introduction:

 

To start with there are four types of text types available, they are:

 

     1) Dynamic Text (D)

     2) Text Element  (E)

     3) Include Text    (I)

     4) Text Module    (M)

 

You can see the same in the below screen shot which gives you a better idea(Marked in Red below):

 

tt1.PNG

 

Now let us look into the purpose of each text types:

 

1) Dynamic Text (D):


Usually we use to display each field explicitly using text element by mentioning the field name. Using the dynamic text type we can display the complete table type which ever is assigned to it. Hope You will get a better understanding once you go through the below screen shot and example.


Now in the below screen shot you can find how the dynamic text is assigned in smartforms.


tt2.PNG



Now this field which is assigned as L_TEXT shoud be declared in global definitions of smartforms whose type is of some Table type created in SE11.

Also all the fields in L_TEXT will be filled either in smartform or in driver program and the values will get displayed accordingly based on the number of fields in the structure of the table type.



2) Text Element (E):


This text type is used for displaying the values where we have to either explicitly mention the variable name or the field name if it is a workarea. This is generally used in most instances.


It will continue to display the value in the field whichever is mentioned explicitly, using this text type we can only display one field value at a time. The below screen shot will help you better on how it needs to be assigned.

 

tt3.PNG

Here GV_HEADER is a variable to display the required value, please note that this variable is used locally and valid only within the smartform which concludes that this text type is for variables confined within the smartforms.

 

 

3) Include Text (I)

 

This text type is used to display the standard text that is created in the T-CODE SO10. Usually we use to read the data created in standard text using READ_TEXT function module, but in smart form we have the flexibility to directly use it. It can be best understood by the below screen shot.

 

tt4.PNG


As we can see above the include text can be used in the smartform directly by giving the header details of the standard text created in SO10. The header details can be taken from SO10 t-code by using the below navigation. As soon as you reach the text maintained in SO10 navigate to:


Goto --> Header.


You will be getting the below pop up. where you can find all the details needed to enter during the include text creation.


tt5.PNG

 

Now whatever text that we have maintained in SO10 will be displayed.

 


4) Text Module (M)


This text type is helpful for using the same text in multiple smartforms. Suppose say we have a header text which will be same for most of the smartforms that we create then first we have to create the text module and then assign them in the smartform.


Lets go step by step. As a first step we need to create a text module, the t-code for this is SMARTFORMS. Click on the text module radio button and click create as shown below.


tt6.PNG



In the next screen enter the required text and save it as shown below.


tt7.PNG


You also have the option to translate the text module text in mutiple languages as shown below in the Management Tab. By clicking on the arrow against the 'selected language' radio button you will find all the available languages.


tt9.PNG



Once you have created, the module text is ready to be used, now select the text type as text module and assign the name as shown below.

 

tt8.PNG


Please note that this same text module can be used in multiple smartforms.


Conclusion:

 

Hope this document gave you a good idea about the different text types that are used in the smartforms. Am sure most of you will have more such things to share, eagerly waiting for your posts.


I will be vary happy for your comments, corrections, ratings etc. Hope this bolg was useful and helped you to increase your knowlege account.


Once again thanks to SCN for its wonderful efforts it has taken till now.


"Lets Learn Share and Collaborate"


Please note that to maintain confidentialy about the client information the screen shots are screened at certain places.



Thanks and Regards,


Satish Kumar Balasubramanian

http://www.asugnews.com/article/sap-custom-abap-code-how-to-ensure-quality-when-outsourcing#printer-friendly css

 

ABAP Code Quality Survey by CAST

 

image001.jpg

 

The other day the American SAP Users group wanted my opinion on a new survey that came out by a group called CAST which had analysed about 70 large custom ABAP applications from organisations in the USA and Europe, and come to the conclusion that the code was a load of old baloney, ludicrous, rubbish, hopeless, fit only for the dustbin.

 

The clear implication was that the ABAP programmers who had written these applications could not touch their own nose, let alone write a program containing even one ounce of quality.

 

Nonetheless the programs worked – they solved the business problem they were created to solve.

 

How can this be?

 

Naturally it is possible to have a working program that is a “big ball of mud” – prone to break for no apparent reason, get hacked and is so complicated it is impossible for any programmer other than the creator (and often even the creator) to understand what is going on, and how to fix things /make changes.

 

I found the survey very interesting on the whole, some of the diagrams were utterly meaningless e.g. “changeability by industry sector” but the general meaning came through clear enough.

 

My first reaction is – they are right. I don’t think anybody is going to disagree, there is a lot of dodgy ABAP programming going on out there. It could be argued that the small sample of programs they used were not statistically significant but I would say that if they had analysed a thousand times more than they actually did the result would have been identical.

 

First off – I just want to express my despair by their use of the term “customization”. Traditionally in ERP circles the term “customizing” was used to refer to making assorted settings in the ERP system (e.g. the IMG in SAP) i.e. no code at all involved and custom programming refers to programs written by SAP customers as opposed to SAP itself. Nowadays everyone in the media using the same term to refer to both so you don’t know whether you are coming or going.

 

Next I notice that 70 of the 71 gigantic custom developed applications analysed were not developed in-house by the organisations being surveyed but rather outsourced. Once again that term means different things to different people but I am making the presumption that here this means that the organisations involved thought they would get their custom applications developed “on the cheap” by hiring some sort of outside organisation, be it in a low cost country or not, that would develop the application for them at the lowest price. As a result the code quality suffered terribly, that comes through from the survey very clearly. I wonder if the results would have been different if more in-house developed programs had been in the mix?

 

I have heard this time and again at conferences and focus groups, from managers who report the horror stories suffered by their organisation who decided to outsource development. This seems like a case of no-one every learning from history.

 

Since the bar at the company I work at is very high indeed, and the people I talk to on the internet (SCN) are at the top of their game it is easy to fall into the trap of thinking most ABAP programmers outside of SAP are good, but I am only dealing with a really small subset.

 

As an example, I am now going to look at the SCN website and see what the latest question posted on the ABAP forum is. Naturally as I write this I do not know what it is, but I do know that when I read it, it is going to make me cry.

 

Here I go.

 

I’m back. It was a question asking how to see who changed a field in a purchase order in SAP. And yes, I want to cry. That is such a basic question, and virtually all the questions are at that level – a month ago someone actually asked “is there an IF statement in the ABAP language”.

 

It seems to me there are thousands of people out there – going by the questions on the forum – who don’t seem to know the vey basics of programming, and yet sometimes preface their question with “I have just been appointed ABAP team leader on a major project for an international company”. Given that sort of thing going on, is it any wonder that the CAST survey uncovers a quality problem?

 

The CAST survey also points out that many programmers are business people who learned ABAP and thus do not know the sort of things you would learn during a computer science degree for example. This is a very true observation. I fall into that basket myself. The difference with me is that after a while (admittedly ten years) I started reading all the “classic” computer books I could (e.g. Clean Code by Robert Martin, Head First Design Patterns by Freeman & Freeman etc…) and it did make me a better programmer – that is what started me off blogging and ended up with me writing a book.

 

Here is a link to the actual survey itself. You have to sign your life away to look at it i.e. give your email details so you get bombarded with advertising.

 

http://www.castsoftware.com/advertising-campaigns/sap-crash-benchmark-report-2015---advertising---v3

 

On page 18 a list is given of all the quality tests that were used to gauge if the code in an application is any good or not.

 

I understand the vast majority of the tests that CAST applied – though some puzzle me and so I am going to ask all you people out there in SCN world for your opinions on the following quality tests suggested by CAST.

 

“Avoid Unreferenced Methods / Unreferenced Functions / Unreferenced Classes” – I have no idea what that means, I looked it up on the internet and it just talked about this in a Java context, implying it was a good and normal thing to do (whatever this may be). What is this, how does this relate to an ABAP context and why is it bad?

 

“Avoid SELECT INTO CORRESPONDING FIELDS OF” – oh dear – I use this all the time unless I am reading a massive amount of data and want every single field in the source table.

 

99% of the time your work area is a lot smaller than the number of columns in the database table you are reading, and there are going to be gaps between them i.e. you might want the primary key and then field 8, then field 12, then field 15.

 

So I thought the idea was to list of columns you wanted in the SELECT statement and do a CORRESPONDING FIELDS OF into the target area.  What’s wrong with that, why is that coming up as a quality violation?

 

Anyway, most of the quality tests seem very sensible tests to me, and tests that a LOT of the custom programs I have seen written would fail. E.g. the wanton cutting and pasting of code, acres of commented out code, EXITS in INCLUDES and the like.

 

Mind you, some of the crimes described can be found in standard SAP code itself. I would be fascinated to see what the result would be if CAST applied the exact same analysis on some large standard SAP applications written in ABAP e.g. sales order entry.

 

I also think SAP may outsource some development, based on the quality of code I see sometimes in OSS notes, specifically the notes that enabled the correct handling of goods receipts for purchase orders with multiple account assignment. The SAP delivered code in that case was riddled with schoolboy errors, and each note needed another note to correct it, a chain of dozens of notes, the final one saying “we give up; you will need to install an enhancement pack to fix this”.

 

Lastly, in outsourced companies, and in-house development, and maybe even at SAP sometimes there seems to be a very lax attitude towards code quality. If I go into an existing program to make a change and find a whole bunch of unrelated errors (unrelated to the change I am supposed to be making) I fix them. This is Robert Martins “Boy Scout Rule” – always leave the code cleaner than when you found it. In some organisations though, this is a sacking offence – i.e. making a change you were not supposed to, even if it fixes a bug or increases the quality of the code (the same sort of quality being measured in the CAST survey). Faced with that sort of attitude, are the survey results any wonder?

 

I apologise for waffling on, and not being as succinct as you might like, but in summary:-

 

·         It seems that yes there is a lot of dodgy, very poor quality ABAP code out there. I have seen it myself.

·         There is no excuse for this at all, there is so much help available on the internet

·         The survey seems to indicate that outsourced development seems to be of particularly bad quality, but since that was virtually all they surveyed that may mean nothing

 

As I said, I would be interested in any feedback regarding the quality tests I mentioned above, that I was unsure about.

 

Cheersy Cheers

 

Paul

 

Scenario-

If outbound delivery for STO is created using BAPI-  BAPI_OUTB_DELIVERY_CREATE_STO, it is not possible to populate value in few fields of header of delivery document (eg.-to display values in administration tab & Shipment tab) by simply passing values in the export parameters and table parameters of BAPI.

import.PNG

tables.png

Solution-

For the values to be populated in delivery header, Badi LE_SHP_DELIVERY_PROC is to be implemented and method FILL_DELIVERY_HEADER is used to populate details of delivery .

The method is called during delivery creation, each time that a new delivery header is filled with data. This method can be used to populate your own delivery header fields.


Solution implementation-

For displaying the desired values in delivery header tabs in delivery document, those values needs to be populated in CS_LIKP.

If these values are coming through any interface (idoc),then it should be exported from the corresponding  function module or program and imported inside method FILL_DELIVERY_HEADER using import statement. This values in turn will be used to fill CS_LIKP to populate the delivery header.

The structure of CS_LIKP is same as LIKP.

One can update header fields using method-FILL_DELIVERY_HEADER, which could not be updated using parameters of BAPI-BAPI_OUTB_DELIVERY_CREATE_STO.

For example- For displaying value in ext.delivery in administration tab and TrnsID code in shipment tab, populate CS_LIKP-LIFEX  and CS_LIKP-TRAID.Data can be used using VL03n-->click on header details icon .

headericon.PNG

adminisstration tab.PNG

shipment.PNG

Similarly, for item level data method- FILL_DELIVERY_ITEM can be used.

Damir Majer

SAP Inside Track Munich

Posted by Damir Majer Jul 5, 2015

The SAP group of regulars Munich (organized within an xing group: https://www.xing.com/net/sap_stammtisch) organize the third SAP Inside Track in Munich.

SAP Inside Tracks are local Events from the SAP Community for the SAP Community. ABAP Developers, Business Consultants, Managers, Students and general interested People in SAP meet each other for this day.

It´s a possibility to broaden your contacts and to share your knowledge and your experience.

 

As a co-organizer (of course I´m ABAP infected ) I´d like to share the Event information in my personal style:

 

 

SAP Inside Track Munich 2015

 

If you like to attend please go direct to http://www.sitmuc.de or follow us on twitter sitmuc

 

Be inspired and get the SAP community feeling!

 

Best regards,

Damir

 

--

Damir Majer -> get in touch via: Damir Majer (@majcon) | Twitter

The following are the various topics that we’ll look at in this blog series: -

  1. Why do we raise exceptions?
    http://scn.sap.com/community/abap/blog/2015/07/03/an-insight-into-exception-handling
  2. General principles when handling/raising exceptions
  3. Different types of exceptions – How do we choose?
  4. Inheritance in exception classes – How do we use this to our advantage?


In the previous blog, we have already looked at why we need to raise exceptions.
In this blog, we will look at the second topic – General principles when handling/raising exceptions.

 

Think locally, don’t make assumptions about the calling program

While implementing a programming unit, we make assumptions about the caller. Sometimes, these assumptions become invalid during the course of time. It is also possible that we have made the incorrect assumptions about the caller in the first place. We therefore not only need to think about what to do if those assumptions are true but more importantly what to do if the assumptions are broken. This not only helps us make robust programs but also programs that are more re-usable.

This can be illustrated by an example program depicted by the following sequence diagram. Please note that some details have been omitted out for the sake of clarity: -

Sequence Diagram.JPG

 

  1. In this example, the calling program first creates an instance of the library (lo_library) and the student lo_student1 (Divyaman).


  2. The calling program then registers the student in the library.


  3. The calling program then creates the book – lo_book1 (Catcher in the Rye) and adds it to the library stock.


  4. The calling program then loans-out the book lo_book1 (Catcher in the Rye) to the student lo_student1 (Divyaman).

The execution of the above program is successful because the pre-requisite for lending a book to a student (the student must be registered as a member of the library) has been met. However, the caller, could mistakenly skip registration of the student. What will happen in that case? Let’s take a look at the source code of the ZCL_LIBRARY=>LEND_BOOK method.Sequence Diagram.JPG

 

The code above contains no check for the registration status of the student trying to loan-out the book from the library. If the student was not registered by the calling program, the library will still end up lending the book to the student. The method was written with the assumption that the student passed to it as a parameter is a registered member of the library.

 

In order to prevent this, we introduce logic within the ZCL_LIBRARY=>LEND_BOOK method to check whether the student is registered. If not, we raise an exception. Subsequently, in the calling program, we handle this exception and print an appropriate message on the screen in case the exception occurs. This is shown in the source code below: -
Sequence Diagram.JPG

Sequence Diagram.JPG

 

Now, when the caller tries to lend books to a student who is not registered as a member, an exception is raised by the ZCL_LIBRARY=>LEND_BOOK method. The caller of this method (ZCL_STUDENT=>LOAN_BOOK) in turn propagates it to the calling program ZEXC_HNDL_EXMPL2 where the exception is handled to print an appropriate message.

Now, when the program is run the following message is printed on the screen: -

Catcher in the Rye could not be loaned.


The program in the previous blog also illustrates this principle.

 

 

But this will never happen

This ties in very closely to the principle ‘Think locally, don’t make assumptions about the calling program’. However, here we are talking about assumptions with regards to the state of the program or the underlying data store. While coding a program unit, we might assume that the program will be in a certain valid state or the data in the under-lying data store will be in a certain valid state at the point where the program unit is executed. However, we often fail to describe the behavior of the program unit should those assumptions prove to be untrue. In order to make our programs more robust, this is a factor that ought to be considered.

 

Let’s consider the following example: -

Sequence Diagram.JPG

 

 

Some details have been left out of the above diagram for the sake of brevity.

  1. Here, we create a library instance – lo_library.
  2. We created three student instances – lo_student1 (Divyaman), lo_student2 (Anubhav) and lo_student3 (Jack). The three students are registered with the library.
  3. Three books are created – lo_book1 (Catcher in the Rye), lo_book2 (The Kite Runner) and lo_book3 (A Thousand Splendid Suns). These are then added to the library stock.
  4. The books are then loaned as follows: -
    Catcher in the Rye -> Divyaman
    The Kite Runner -> Anubhav
    A Thousand Splendid Suns -> Jack
  5. The student and the book loan information is then written into a text file in the following format: -
    SN-Student 1’s Name
    <Blank Line>
    SN-Student 2’s Name
    <Blank Line>
    ….
    ….
    SN-Student N’s Name
    <Blank Line>
    BL-<Book 1’s Title>-<Book 1’s Author>-<Student Member Name>
    BL-<Book 2’s Title>-<Book 2’s Author>-<Student Member Name>
    ….
    ….
    BL-<Book N’s Title>-<Book N’s Author>-<Student Member Name>

    However, the programmer makes an error while coding the WRITE_TO_FILE method of the ZCL_LIBRARY class. The source code of this method is as follows: -
    Sequence Diagram.JPG

The highlighted code-fragment should have been before the IF condition. Because of this, the name of the first student member ‘Divyaman’ does not get written into the text file.
The contents that actually get written into the text file are as follows: -

SN-Anubhav

 

SN-Jack

 

BL-Catcher in the Rye-J D Salinger-Divyaman

 

BL-The Kite Runner-Khaled Hosseini-Anubhav

 

BL-A Thousand Splendid Runs-Khaled Hosseini-Jack

 

Another program reads the data from the text-file (created by the program above) to re-create the students, books and the book-loans. This is achieved by calling the ZCL_LIBRARY=>READ_FROM_FILE method. The source code of this method is as follows: -

Sequence Diagram.JPG

 

This method assumes that the data in the source text file is correct.
Based on this assumption, it: -

  1. Reads library member names and creates student instances for them.
  2. It adds the student instances to the instance internal table PIT_STUDENTS.
  3. It then adds each of the loans to the internal table PIT_BOOK_LOANS.
    The side-effect of this is that while we don’t have ‘Divyaman’ as a registered student (in in the internal table PIT_STUDENTS), we do have a book loaned-out in his name.

The problem here is that it was assumed during file read that the data in the source text-file would be correct. The possibility of a book-loan to a student who is not registered was not considered, because it was assumed that this would have been catered to and validated by the method WRITE_TO_FILE.
Ideally, it should have been verified in the READ_FROM_FILE method that each book is loaned to a student that is registered. If not, an exception must be raised.

The modified source-code is as follows: -

Code.JPG

code2.jpg

In the above source code, the exception raised by the method SEARCH_STUDENT_BY_NAME instead of being handled by a blank handler has been propagated to the calling program since further processing cannot continue if the student has not been found. The calling program, in turn, prints an error message on the screen in the event of an exception.

 

Throw Early, Catch Later

When an erroneous state is encountered in a program unit, we must throw an exception right at that point because it is here where you will have the most precise information about the error along with the context in which the exception has occurred.

Code.JPG 

 

Let’s assume that the SEARCH_STUDENT_BY_NAME method does not raise exceptions and has the following source code: -

Code.JPG 

In the code above, the method will return a blank student instance in the parameter EX_STUDENT if either the name (IM_NAME) is blank or a student matching that name is not found in the internal table PIT_STUDENTS.

It is the calling method READ_FROM_FILE which has been made responsible for raising an exception in response to a blank student instance. The code in READ_FROM_FILE is as follows: -
code2.jpg  

In the above program, the ZCX_STUDENT_BY_NAME exception is raised if a blank student instance is returned by the SEARCH_STUDENT_BY_NAME method. However, please remember that in the case of books which have not been loaned-out, the student name will not be mentioned. Therefore, a blank student instance is a perfectly valid scenario in those cases.

The problem here is that the error occurred in the SEARCH_STUDENT_BY_NAME method while the exception is being raised much later in the READ_FROM_FILE method. By that time an important piece of information is lost – Was the student instance returned as blank because the student name was blank or was it returned as blank because a student with that name wasn’t found. If it’s the former, it is perfectly fine for the READ_FROM_FILE method to continue with further processing. If it’s the latter, it is not possible for the READ_FROM_FILE method to continue processing.


This problem can be fixed by raising the exception early in the SEARCH_STUDENT_BY_NAME method itself. This is shown in the code snippet below: -

code2.jpg

 

In the calling method, we can then introduce exception handling as follows: -

code2.jpg

Here, the method propagates the ZCX_STUDENT_NOT_FOUND exception and has a blank handler for the ZCX_BLANK_STUDENT_NAME since we do want to continue processing if the student name against a book is blank.

 

When it comes to handling exceptions, the calling program might do any of the following: -

  1. Catch the exception.
  2. Propagate the exception.
  3. Catch the exception and raise either a new one or add more information to the exception before raising it again.

This can be seen in the above example also. The SEARCH_STUDENT_BY_NAME method finds the student instance from the PIT_STUDENTS instance internal table. If the ZCX_BLANK_STUDENT_NAME exception is generated, it is handled in the calling method (READ_FROM_FILE).
If the ZCX_STUDENT_NOT_FOUND exception is raised, the READ_FROM_FILE method cannot continue with further processing and the exception is therefore propagated. The calling program catches the exception and decides to display an error message should the exception be raised.

 

As a concluding remark to this blog in the series, let me emphasize that these are principle/guidelines that need to be considered when thinking about exception handling. Application of these principles without thought could prove to be an over-kill on certain occasions and may result in bloated code.

... create clean, self documenting, highly-performant, technical-debt free, non-redundant & re-usable code!)

 

Sadly, The Beastie Boys couldn't get anyone at their record label to sign off on this track and we had to suffer their re-worked version.  However, the underlying message is one I'm keen to re-iterate to our vast global ABAP community.

 

As a moderator of the ABAP space here on SCN, I see a LOT of shall we say "interesting" discussions, where it is apparent a relatively junior coder is struggling with how to answer a functional requirement or deliver on a specific set of functionality.  I'm not talking about the "I'm new to ABAP, please tell me how to tie my own shoelaces" posts - I mean those people who now know enough about ABAP and SAP to question the requirements they are given but don't quite feel confident or experienced enough to question why they are being told to do what they are.

 

Your job isn't to say NO, it is to point out there may be a better way

SayNoMakeBetter.png

I saw this tweet just a few days ago and felt it was very relevant to this post's topic.  Many people will devote most of their efforts to learning new and exciting technical abilities, but will ignore inter-personal and social skills.  As a result, they get into a situation where they don't know how to deal with someone asking them to do something they firmly believe is wrong.  I see it especially in the off-shore side of our industry, where "leads" appear to be making shocking decisions on what should be built and how it should be built, and the poor "junior" is left stuck between doing what they are told, or what they know is better.

 

So, for the sake of your own longer term career and for the sake of the SAP system you are working on, please learn to stand up for your own opinions when it comes to bad design.  Just because you are the junior and your lead has told you to do something, really doesn't make it right.  There are lots and lots of documented examples of where this just isn't the case here on SCN.

 

I'm not advocating arguing with your lead over everything but as the tweet above suggests, it is about educating those around you that there is a better way.  Sadly, in some cases this may end with the lead pulling rank and just telling you to do what you are told - in such cases, I'd suggest you start to also brush up on your CV writing skills and get networking on LinkedIn, as that isn't the sort of Lead you want to be around for long...  I'd also suggest you make sure you document your objections in an email to your lead, so that if/when the proverbial hits the fan you can at least defend yourself.

 

This isn't much of a technical post, however that's kind of an underlying point - being good at ABAP (indeed anything) isn't just about knowing the language inside out.  Devote some of your efforts to softer skills and develop the ability to deal with conflict and disagreement in a team.  You never know, you might just find yourself taking up the role of lead

Preface

In this series, we look at the various factors that need to be considered when using class-based exceptions. The series is not an introduction to class-based exceptions. It assumes that the reader is familiar with the basics and has used class-based exceptions before. The intention of this series is to provide our own insights which would hopefully help the programming community make more informed choices with regards to exception handling. However, it should be pointed out that the content of this blog has also been influenced by various other blogs/articles written by other developers on this topic. Some of those have been listed below: -

  1. http://scn.sap.com/people/thomas.weiss/blog/2005/03/22/the-new-class-based-exception-handling-in-abap--part-1

 

The following are the various topics that we’ll look at in this blog series: -

  1. Why do we raise exceptions?
  2. General principles when handling/raising exceptions
    An Insight into Exception Handling - 2
  3. Different types of exceptions – How do we choose?
  4. Inheritance in exception classes – How do we use this to our advantage?

 

In this blog, we’ll look at first topic – Why do we raise exceptions?

 

 

Example Scenario

The principles of exception handling have been explained using the following ‘Library’ example: -

The library is a part of a university and has books which are available on loan to its members. 
All students of the university are, however, not its members. Students need to be registered as members in the library before they can loan books.

The library is responsible for registering students as members. The library is also responsible for de-registration of students. However, de-registration must only be possible if books loaned by the student have been returned. The library is responsible for lending books that members want to borrow. It is also responsible for receiving books that members want to return. A book which has already been loaned-out cannot be loaned by another member unless it has been returned back to the library by the member. Additionally, the library does add new books to its stock or may remove existing books from its stock (if they have damaged). Each book is uniquely identified by its title (This is not realistic but we want to keep the example simple) and can have only copy.

The student must get himself registered as a member of the library if he wants to loan books from the library. Each student is uniquely identified by his name. The student can loan books and also return books back to the library. The student can get himself de-registered (provided he doesn’t have any un-returned books) from the library.

The following class diagram depicts the various classes: -

Class Diagram.JPG

Why do we throw exceptions?

All program units (method/FM) generate outputs. By, outputs we are not only referring to the exporting and changing parameters of the program units, but also referring to any target (file, internal table, database table…etc.,) that the processed data could be written into.

The output has a domain i.e.., a list of possible values which indicate the result of execution of the program unit. If during the execution of the method/FM, a state is reached which cannot be adequately described by any value in the output domain, and as a result of which further execution cannot continue, we know that we have encountered an exceptional state. And, throwing an exception would be a good idea at this point.

This can be illustrated by an example program depicted by the following sequence diagram. Please note that some details have been omitted out for the sake of brevity: -
 
Sequence Diagram.JPG
 

  1. In this example, the main program first creates an instance of the library (lo_library) and the student lo_student1 (Divyaman).

  2. We then create three books – lo_book1 (Catcher in the Rye), lo_book2 (The Kite Runner) and lo_book3 (A Thousand Splendid Suns).

  3. The books lo_book1 (Catcher in the Rye) and lo_book2 (The Kite Runner) are then added to the library stock.

  4. The book lo_book1 (Catcher in the Rye) is then loaned-out by the student lo_Student1 (Divyaman).

  5. The main program then determines which of the three books have been loaned out. It calls the IS_BOOK_LOANED method to determine that. The following result is printed on the screen: -
    Catcher in the Rye  has already been loaned-out
    The Kite Runner  is available to be loaned out
    A Thousand Splendid Suns  is available to be loaned out

    The result is correct for ‘Catcher in the Rye’ and ‘The Kite Runner’ but not for ‘A Thousand Splendid Suns’. This book is not even available in the library’s stock and therefore cannot be loaned-out.

    The reason for this incorrect message for ‘A Thousand Splendid Suns’ is explained below.

    The source-code of the IS_BOOK_LOANED method is as follows: -
    DATA: lv_current_title    TYPE string,
    lv_is_available    
    TYPE boole_d.

    CALL METHOD find_loan_by_book
    EXPORTING
    im_book      
    = im_book
    IMPORTING
    ex_book_loan 
    = lwa_book_loan.

    IF lwa_book_loan-student IS NOT INITIAL.

    ex_is_loaned
    = abap_true.

    ELSE.

    ex_is_loaned
    = abap_false.

    ENDIF.

    The IS_BOOK_LOANED method (source-code) assumes that the book for which loan-assignments are being checked exists in the library stock. In the case of ‘A Thousand Splendid Suns’, that is not the case. However, The EX_IS_LOANED parameter is returned as blank misleading the caller to believe that the book can be loaned-out since it hasn’t already been loaned.

    The output (EX_IS_LOANED) can only be ‘X’ (The book has been loaned) or space (The book has not been loaned). The situation encountered here (The book is not in library stock) cannot be described accurately using any of the values available in the output domain. Therefore, it is essential that we introduce an exception that indicates the unavailability of the book.


  6. The IS_BOOK_LOANED method can be changed as follow: -
    If the book does not exist in stock, we’ll raise an exception. Otherwise, we continue with the rest of the code.
    The caller can then catch this exception and display a message to indicate that the book is not available in the library.
    The source code of IS_BOOK_LOANED is as follows after the changes: -
    Definition: -
        METHODS is_book_loaned
      
    IMPORTING
        im_book        
    TYPE REF TO zcl_book
      
    EXPORTING
        ex_is_loaned   
    TYPE boolean
      
    RAISING
        zcx_book_loans_exc
    .

    Implementation: -

          DATA: lv_current_title    TYPE string,
           lv_is_available    
TYPE boole_d.

    
DATA: lwa_book_loan       TYPE wa_book_loan.

    
CALL METHOD is_book_available
     
EXPORTING
       im_book        
= im_book
     
IMPORTING
       ex_is_available
= lv_is_available.

    
IF lv_is_available = abap_false.

     
RAISE EXCEPTION TYPE zcx_book_loans_exc.

    
ENDIF.

    
CALL METHOD find_loan_by_book
     
EXPORTING
       im_book      
= im_book
     
IMPORTING
       ex_book_loan 
= lwa_book_loan.

    
IF lwa_book_loan-student IS NOT INITIAL.

      ex_is_loaned
= abap_true.

    
ELSE.

      ex_is_loaned
= abap_false.

    
ENDIF.

    Calling Program: -
       
TRY.

   
CALL METHOD lo_library->is_book_loaned
     
EXPORTING
       im_book      
= lo_book3
     
IMPORTING
       ex_is_loaned 
= lv_is_loaned.

    IF lv_is_loaned = abap_true.

     WRITE: /, lo_book3->get_title( ), ' has already been loaned-out'.

    ELSE.

     WRITE: /, lo_book3->get_title( ), ' is available to be loaned-out'.

    ENDIF.

   CATCH zcx_student_reg_dereg_excs.

    WRITE: /, lo_book3->get_title(), 'is not available in the Library.

   ENDTRY.

   Exception handling has also been introduced at the points where the IS_BOOK_LOANED method was called for books lo_book1 (Catcher in the Rye)
       and lo_book2 (The Kite Runner). Those code-snippets have not been included here for the sake of brevity.

       The result of program execution after code changes is as follows: -
       Catcher in the Rye  has already been loaned-out
       The Kite Runner  is available to be loaned out
       A Thousand Splendid Suns  is not available in the library



Preface

There is sometimes a debate about whether to use a pure Static Class or a Singleton Class. The obvious question here would be - Why are we even comparing a Static Class with a Singleton Class in
the first place? Well that’s a fair question.
Let’s first define a Singleton Class. A Singleton class is a class which can have at the most one instance.
A Static Class, on the other hand, contains only static methods and attributes and therefore does not require instantiation.

One could argue that in most cases, if not all, a static class can imitate the behavior of a Singleton class fairly well. Also, defining and consuming a Static Class is simpler than implementing a Singleton class. But, an overly simplified approach may not always be an apt option.

It seems that in quite a few cases either of the two can be used. This creates confusion in our minds about which one to use. This blog is intended to unravel the mystery that has been looming in some our minds for some time now.

 

Two sides of the same Coin or not

In an Object oriented world, a Class is more like a template or a definition for an entity. Objects are the result of instantiating a Class which represent entities. For example, Student is class where you define the attributes and operations whereas Mike, Smith or Jane is objects of Student class which are a result of instantiating the Student class. They will off course behave as defined in the Student class.

  A Singleton Class is a design pattern first formulated by Gang of Four. A property of Singleton class which diversifies it from a regular class is that the implementation of Singleton class ensures that it can be only instantiated once. One example of a Singleton class is a Logger class which can be globally consumed within multiple sub-systems for logging information and warning messages occurring during an end-to-end process. As an application developer you don’t want to instantiate more than one instances of Logger class because then either each sub-system will have their own loggers or they will not have the correct instance of logger class to feed the messages.


ClassDiagram.jpg


Most, if not all, of the Object Oriented programming languages would support static attributes and methods in a Class. As mentioned earlier, classes are the skeleton which defines how the objects will behave once instantiated. An attribute or method is defined as static if it is to be shared amongst all the instances of a class. So, a static attribute will hold the same information across all the instances of the class compared to an instance attribute where each instance will hold its own value. Consider an example of Webpage which wants to count the no. of hits to the page on Internet will model a static attribute to hold the no. of visits. Similarly, methods or functions can be modeled as static which contains one source of truth for all the instances of a class. For obvious reasons, a static method will only be able to access the static parts of the class.

 

Mostly we get thrilled and find it convenient to create pure static classes altogether. One such example is a Database Layer class representation where we implement all the open SQL queries. The Business Logic Layer class can call static methods on this DB layer class for any SQL queries that it want to execute on the Database. This way we also segregate the responsibilities and areas of concern.

By the way, a pure static class would be wherein all the attributes and methods are static and there is no scope of instantiation.

There is little to choose between the two, Singleton and Static class. Singleton implementation will always restrict the no. of its instances to one, provided it is implemented correctly. And a static class will not have an instance at all but, will be able to implement the same functionality as Singleton using Static methods and attributes.


 

Unraveling the Mystery

I hope explanation in the previous sections is clear as to why we are comparing Singleton with Static classes. Now, let us finally look at what are the challenges when taking the simpler approach of implementing a Static class in lieu of Singleton.

     1.     Polymorphism is a very compelling concept of Object Oriented programming. Polymorphism allows you define an interface and implement that in many ways. And at runtime you can determine, which implementation should be called. This can be achieved with Inheritance, another very significant concept in OOPS. With all the methods and attributes of a class defined as static it cannot be sub-classed. If I have decided to implement a pure Static Class then, I must be very sure that I will never have to subclass it. Law of Nature is also that the requirements are never completely stated which implies that changes are inevitable. A static class can never reap the benefits that Polymorphism has given to this World. It may be worthwhile to keep the option open to allow polymorphism for future even if it is not required to start with.

 

 

     2.     Initializing a Static Class pose another challenge as compared to a Singleton Class. For a Static Class it would not make much of sense to implement an instance Constructor. As discussed before, an instance of a Static class will have no members which are instance specific and misleading to create instances of such a class. Even if, we were to create instances of a Static Class then also, its instance Constructor would initialize the same shared static members over and over again possibly producing incorrect results. The only relevant placeholder for initializing such a class would be a Class Constructor or a Static Constructor. One disadvantage in a Class Constructor is that it does not have exporting or importing parameters and it does not allow to propagating exceptions.

 

     3.     Finally, object oriented programming is based on the concept of explicitly instantiating objects of a class but, in case of pure static classes there is no instantiation of the class. The static classes are loaded by the compiler when the class is first encountered in the code by calling its implicit or explicit Class Constructor. A disadvantage from memory consumption perspective is that there is no way to explicitly free up the memory space occupied by static classes. The class stays in the memory till the internal session exists. In short, we can neither control when the static class should be initialize nor we can free up the memory taken my static class when we want to.

 

 

It would be interesting to revisit the Logger example to understand if there is any real advantage of using it as a Singleton instead of a Static Class.

Initially the requirement was to have a Logger class which will hold information, warning and error messages for the entire application during a session and displays to the user. The messages will span across all the sub-systems and share the same Logger Class.

Let us create a Singleton Class to map the Logger utility.

It is important to define the Instantiation as ‘Private’ for a Singleton class as a first step to ensure that CREATE OBJECT cannot be called from outside the Logger class.

Also, it is good programing practice to make the Singleton Class as ‘Final’. If the class is not declared Final then it is allowed for inheritance. It will be a bad design to allow sub classing for a Singleton class. Moreover, Singleton property of the Superclass is not automatically inherited by Subclass so we end up allowing multiple instantiation of the Subclass.

 

ClassDefinition.jpg

 

Define a private class attribute to hold the reference of this Logger class.

ClassAttributes.jpg


Now, create a Class method to streamline the instance creation process which controls that the Logger class can be instantiated only once. Remember, it is imperative that we have only one instance of the Logger to collect all the messages for the Application.

ClassMethods.jpg



Implement the GET_INSTANCE method to enforce Singleton behavior.

GetInstance.jpg



Now, add Logger specific functionality to this Singleton Class.

OtherMethods.jpg


All went fine till the requirements changed one fine day. As per the new requirement, we need to have separate Logs created for each of sub-systems. Messages from some sub-system had to be displayed to the user and for few they had to be written to a file.

Based, on the above requirement, I am required to remove the singleton constraint from my existing Logger class and allow public instantiation. This is to allow creation of one Logger instance per sub-system which allows segregating and de-coupling behavior of message output. One option is also to use inheritance to subclass Logger class for messages to be written to a file. In future, if the requirement changes again and there are new target for capturing messages from a sub-system, I would merely need to extend the Logger class and implement the specific behavior.

Had we chosen to use a pure Static Class to implement the Logger in the first place, it would have been difficult to manage the ever changing requirements. We would have ended-up adding many static methods in the same class which are performing disjoint operations failing the Single Responsibility principle. Achieving the requirement of multiple Log handlers, one for each sub-system, would perhaps have made the code hard to maintain and extend.

Implementing a pure static class is not an Objected Oriented way of solving a problem and hence, does not allow to fully exploiting the capabilities of Object Oriented design principles. On the other hand, implementing a Singleton class may be a touch complex than Static Class but, it allows keeping your design open for enhancements and closed for modifications which are an important OO Design Principle. 

It is still alright to solve a very trivial problem through Static Class but, the moment we realize that non-trivial requirements are creeping through; it is time to go for a Singleton.




Juwin Pallipat Thomas

Screen hacks

Posted by Juwin Pallipat Thomas Jul 2, 2015

Would you believe, if I said that the screen below, is a selection screen and not a dynpro?

untitled.png

I could find a lot of posts on the web, on how to include a selection screen, inside a dynpro.

 

http://scn.sap.com/thread/199048

http://scn.sap.com/thread/1340932

http://scn.sap.com/message/6804269

and so on….

 

But, I couldn’t find any posts on how to include a dynpro inside a selection screen. Standard way of working with this is, by using a tabbed block on selection screen and including the dynpro inside one of the tabs.

 

http://scn.sap.com/thread/1384476

 

Dynpro have capabilities to have table controls, custom controls, subscreen etc, which are not available on selection screen. Thus, an option to add a dynpro to the selection screen, can give you endless possibilities to play around on the selection screen.

 

A dynpro can be included in another dynpro, using a subscreen area. So, a dynpro can be included on a selection screen, if there is a way to create a subscreen area on the selection screen.

 

The way to create a subscreen area on the selection screen, is by creating a tabbed block.

SAP help gives the sample code to create a tabbed block, as:
SELECTION-SCREEN: BEGIN OF TABBED BLOCK mytab FOR 10 LINES,
  TAB (20) button1 USER-COMMAND push1 DEFAULT SCREEN 100,
  TAB (20) button2 USER-COMMAND push2 DEFAULT SCREEN 200,
  END OF BLOCK mytab.

 

What this does is, it creates a subscreen area named mytab on the selection screen, with two tabs named button1 and button2. What this also does is, it creates a workarea inside the program, with name mytab, containing 3 fields, namely PROG, DYNNR and ACTIVETAB. When the 1st pushbutton is pressed, mytab-dynnr is assigned 100 and when the 2nd pushbutton is pressed, mytab-dynnr is assigned 200. Thus, when a person presses on each tab, the subscreen called in the subscreen area is different.

 

A sample output may look like

2.png

 

Now, I may include the dynpro in one of those tabs and the output may look like

3.png

 

In many cases, I do not have more than one dynpro to include on the selection screen. So, I don't need more than one tab. So, if I remove the unwanted tabs and keep the dynpro in one of the tabs, the output may look like.

4.png

Needless to say, this looks a bit akward.

 

So, I remove the final tab also, and just keep the frame, i.e., my code is reduced to just


SELECTION-SCREEN: BEGIN OF TABBED BLOCK mytab FOR 10 LINES,
  END OF BLOCK mytab.

 

If I just execute the program now, it will result in a dump

5.png

This because, the program doesn’t know which subscreen needs to be called.

 

Note that, in the code,
SELECTION-SCREEN: BEGIN OF TABBED BLOCK mytab FOR 10 LINES,
  TAB (20) button1 USER-COMMAND push1 DEFAULT SCREEN 100,
  TAB (20) button2 USER-COMMAND push2 DEFAULT SCREEN 200,
  END OF BLOCK mytab.


The bold portion creates the subscreen area named mytab and the other portion creates the two tabs. So, when I remove the code for tabs, the subscreen area is not removed from the selection screen.

 

In order to avoid the dump, I must say what is the subscreen to be included in the subscreen area. Since mytab is also a workarea in the program, I can assign the subscreen number to that workarea, from within the program. To do that, in the INITIALIZATION or AT SELECTION-SCREEN OUTPUT event, I can assign mytab-prog = sy-cprog and mytab-dynnr = 100 or the dynpro screen number.

 

My final output will look like

 

6.png

Thus, using just the subscreen area, and not using the tabs, I can include a dynpro screen on a selection screen. Since I can have many other complex screen elements on a dynpro screen, I can have all those elements on the selection screen also.

Hello community,

 

For a long time now I have been asking myself why the ABAP correction instructions in SAP Notes are presented in the way they are, pure black and white text.

 

Sure, there are other ways to access the correction instructions that make it easier to view and understand the changes, but wouldn't it be nice if we could review the code changes directly in the note text in SMP?

 

Well, with this in mind I have taken some time to develop a chrome extension to enhance the presentation of ABAP CIs, and this is the result:

image920_no_pilot.png

The extension can be installed from the chrome web store:

SAP Note Enhancer - Chrome Web Store

 

The code is open source and it can be accessed in GitHub:

dellagustin/SAP_Note_Enhancer · GitHub

 

I hope you find this helpful.

If you find bugs, missing keywords or have suggestions for features, please create an issue in the GitHub page.

 

UPDATE

 

I have now also created a Greasemonkey user script that works in a similar way for Firefox.

It does not have all the features from the chrome extension but can already be quite handy.

In order to install it you need to have the Greasemonkey add on installed in Firefox, then visit the link:

https://raw.githubusercontent.com/dellagustin/SAP_Note_Enhancer/firefox_gm_user_script/greasemonkey/sap_note_enhancer.us…

 

Best Regards,

Guilherme.

This has been a SALV Editable week. Earlier this week, I published a blog on standard application using SALV for editable(SALV Editable? Yes, as per this Standard SAP Application)

 

The solution works great if there is an extra button. This extra button – say Edit – would make the SALV editable. This button is required to gain the access of the underlying Grid object (CL_GUI_ALV_GRID). The Grid object than used to set up the editable functionality. Paul Hardy had asked if we can remove that extra button and make the SALV editable directly, in his comment section of my previous blog SALV Editable? Yes, as per this Standard SAP Application

 

 

No more button

The idea was to find an event which gets triggered but before the output is displayed to the user. I did some research, infect lot of research to find an event that would work in both Grid and FullScreen ALV. I played with different events like TOP_OF_PAGE, TOP_OF_LIST, PRINT_TOP_OF_PAGE etc.

 

Fullscreen ALV triggers TOP_OF_PAGE but not the Grid ALV, as for Grid there is no direct TOP_OF_PAGE. FullScreen ALV triggers it when the top of page is created using the method O_SALV->SET_TOP_OF_LIST( ). Also this would bring up a header in the ALV. For the Grid, You would need to create an object for CL_DD_DOCUMENT, pass it to the Grid object and raise the event TOP_OF_PAGE. Without having the Grid to begin with, can’t assign the header, and thus can’t get the TOP_OF_PAGE event to work.

 

Event AFTER_REFRESH is the trick, I was looking for

SALV_Editable_0.png

 

Event AFTER_REFRESH

Event AFTER_REFRESH gets triggered after system finished rendering ALV – Field catalog parsed, Data sent to the DP (data provider) aka Frontend, Layout applied, Toolbar created, and all other ALV related stuff. Now, how to capture this event when there is no Grid object. So, declared an event handler to handle the event AFTER_REFRESH of class CL_GUI_ALV_GRID. The trick is the registration of the event using the addition FOR ALL INSTANCES of SET EVENT HANDLER.

 

  "To gain an access to the underlying object and
  "  do the magic
  SET HANDLER lo_event_h->on_after_refresh
    FOR ALL INSTANCES
    ACTIVATION 'X'.

 

FOR ALL INSTANCES is quite powerful. It can register the event for any object for that event belongs, even if the object is instantiated after the handler is registered. That’s very useful in this scenario as the Grid object is not yet instantiated.

 

 

Avoid Endless Loop Trap

To make the grid editable, either you can pass the LAYOUT-EDIT = ‘X’ with following REFRESH_TABLE_DISPLAY( ) call or method SET_READY_FOR_INPUT call . The thing is, both of these methods would call the method SOFT_REFRESH_TABLE_DISPLAY and would raise the event AFTER_REFRESH. This would again be caught in our event handler. The event handler would make the grid editable again with refresh – so going in the loop.

 

To avoid this, we would switch off – more technically deregister the event handler. Using the addition ACTIVATION space in the SET HANDLER would do this for us.

 

    "deregister the event handler
    SET HANDLER me->on_after_refresh
      FOR ALL INSTANCES
      ACTIVATION space.

 

 

 

Toolbar

Next is the toolbar. The edit buttons in the toolbar are displayed without any additional work, but for grid there is some special logic which prevents it from creating the edit buttons in the toolbar. This all happens in the method CL_SALV_CONTROLLER_EVENTS=> RAISE_BUILD_UIFUNCTION where only few of the buttons are displayed.

 

So we will trick the toolbar to add the same missing buttons. To do this, we would need to register the event TOOLBAR for the CL_GUI_ALV_GRID. This would be done same – with using the FOR ALL INSTANCE.

 

  "only for GRID, would need to add the toolbar buttons
  IF io_salv->get_display_object( ) = 3.
    SET HANDLER lo_event_h->on_toolbar
      FOR ALL INSTANCES
      ACTIVATION 'X'.
  ENDIF.

 

 

Single Method to Make it Editable

Not out of the box single method :), but I enclosed all these logic in one single method.  Call this method by passing the ALV object before the DISPLAY method, and all done.

 

The method would register the event AFTER_REFRESH and TOOLBAR (for grid). The class also host the event handler methods in the local class. The event handler would make the grid editable and add the buttons in the toolbar as well. All the complexity for making it editable is hidden and wrapped in this method.

 

Also this method would control the logic for multiple SALV objects. Optional parameter SENDER for the event handler with the object pool design pattern is used.

 

" Make grid editable
" call before display
  zcl_test_np_salv_model=>set_editable( gr_table ).

 

In Action

Fullscreen ALV generated by SALV in Editable mode

SALV_Editable_2.png

 

ALV Grid generated by SALV in Editable mode

SALV_Editable_2_0.png

 

Multiple Grid, only one is editable

SALV_Editable_1.png

 

Full Method Code

Here is the method ZCL_TEST_NP_SALV_MODEL=>SET_EDITABLE

class ZCL_TEST_NP_SALV_MODEL definition
  public
  final
  create public .

public section.
  class-methods SET_EDITABLE
    importing
      !IO_SALV type ref to CL_SALV_TABLE .
  PROTECTED SECTION.
  PRIVATE SECTION.
    class-data o_event_h type ref to OBJECT.
ENDCLASS.



CLASS ZCL_TEST_NP_SALV_MODEL IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_TEST_NP_SALV_MODEL=>SET_EDITABLE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IO_SALV                        TYPE REF TO CL_SALV_TABLE
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD set_editable.

  DATA: lo_event_h TYPE REF TO lcl_event_handler.

  "Event handler
  IF zcl_test_np_salv_model=>o_event_h IS NOT BOUND.
    CREATE OBJECT zcl_test_np_salv_model=>o_event_h
      TYPE lcl_event_handler.
  ENDIF.

  lo_event_h ?= zcl_test_np_salv_model=>o_event_h.
  APPEND io_salv TO lo_event_h->t_salv.

  "To gain an access to the underlying object and
  "  do the magic
  SET HANDLER lo_event_h->on_after_refresh
    FOR ALL INSTANCES
    ACTIVATION 'X'.

  "only for GRID, would need to add the toolbar buttons
  IF io_salv->get_display_object( ) = 3.
    SET HANDLER lo_event_h->on_toolbar
      FOR ALL INSTANCES
      ACTIVATION 'X'.
  ENDIF.

ENDMETHOD.
ENDCLASS.

 

 

And the event handler LCL_EVENT_HANDLER

*----------------------------------------------------------------------*
* Event handler ALV events
*----------------------------------------------------------------------*
CLASS lcl_event_handler DEFINITION.
  PUBLIC SECTION.
    METHODS:
      on_after_refresh FOR EVENT after_refresh OF cl_gui_alv_grid
        IMPORTING
          sender,
      on_toolbar      FOR EVENT toolbar      OF cl_gui_alv_grid
        IMPORTING
          e_object
          e_interactive
          sender.
    DATA: t_salv TYPE STANDARD TABLE OF REF TO cl_salv_table.
ENDCLASS.                    "lcl_event_handler DEFINITION
*
CLASS lcl_event_handler IMPLEMENTATION.
  METHOD on_after_refresh.
    DATA: lo_grid TYPE REF TO cl_gui_alv_grid.
    DATA: ls_layout TYPE lvc_s_layo.
    DATA: lo_salv TYPE REF TO cl_salv_table.

    TRY .
        LOOP AT t_salv INTO lo_salv.
          lo_grid = zcl_test_np_salv_model=>get_grid( lo_salv ).
          CHECK lo_grid EQ sender.

          "deregister the event handler
          SET HANDLER me->on_after_refresh
            FOR ALL INSTANCES
            ACTIVATION space.

          "Set editable
          ls_layout-edit = 'X'.
          lo_grid->set_frontend_layout( ls_layout ).
          lo_grid->set_ready_for_input( 1 ).
        ENDLOOP.
      CATCH cx_salv_error.
    ENDTRY.
  ENDMETHOD.                    "on_AFTER_REFRESH
*
  METHOD on_toolbar.

    DATA: lo_grid TYPE REF TO cl_gui_alv_grid.
    DATA: ls_layout TYPE lvc_s_layo.
    DATA: mt_toolbar TYPE ttb_button.
    DATA: ls_toolbar LIKE LINE OF mt_toolbar.
    DATA: lo_salv TYPE REF TO cl_salv_table.


    TRY .
        LOOP AT t_salv INTO lo_salv.
          lo_grid = zcl_test_np_salv_model=>get_grid( lo_salv ).
          IF lo_grid EQ sender.
            EXIT.
          ELSE.
            CLEAR lo_grid.
          ENDIF.
        ENDLOOP.
      CATCH cx_salv_msg.
        EXIT.
    ENDTRY.

    CHECK lo_grid IS BOUND.
    CHECK lo_grid->is_ready_for_input( ) = 1.


*... Toolbar Button CHECK
    CLEAR ls_toolbar.
    ls_toolbar-function    = cl_gui_alv_grid=>mc_fc_check.
    ls_toolbar-quickinfo  = text-053"Eingaben prfen
    ls_toolbar-icon        = icon_check.
    ls_toolbar-disabled    = space.
    APPEND ls_toolbar TO mt_toolbar.


*... Toolbar Seperator
    CLEAR ls_toolbar.
    ls_toolbar-function    = '&&SEP01'.
    ls_toolbar-butn_type  = 3.
    APPEND ls_toolbar TO mt_toolbar.

*... Toolbar Button CUT
    CLEAR ls_toolbar.
    ls_toolbar-function    = cl_gui_alv_grid=>mc_fc_loc_cut.
    ls_toolbar-quickinfo  = text-046"Ausschneiden
    ls_toolbar-icon        = icon_system_cut.
    ls_toolbar-disabled    = space.
    APPEND ls_toolbar TO mt_toolbar.

*... Toolbar Button COPY
    CLEAR ls_toolbar.
    ls_toolbar-function    = cl_gui_alv_grid=>mc_fc_loc_copy.
    ls_toolbar-quickinfo  = text-045.                        " Kopieren
    ls_toolbar-icon        = icon_system_copy.
    ls_toolbar-disabled    = space.
    APPEND ls_toolbar TO mt_toolbar.

*... Toolbar Button PASTE OVER ROW
    CLEAR ls_toolbar.
    ls_toolbar-function    = cl_gui_alv_grid=>mc_fc_loc_paste.
    ls_toolbar-quickinfo  = text-047.
    ls_toolbar-icon        = icon_system_paste.
    ls_toolbar-disabled    = space.
    APPEND ls_toolbar TO mt_toolbar.

*... Toolbar Button PASTE NEW ROW
    CLEAR ls_toolbar.
    ls_toolbar-function    = cl_gui_alv_grid=>mc_fc_loc_paste_new_row.
    ls_toolbar-quickinfo  = text-063.
    ls_toolbar-icon        = icon_system_paste.
    ls_toolbar-disabled    = space.
    APPEND ls_toolbar TO mt_toolbar.

*... Toolbar Button UNDO
    CLEAR ls_toolbar.
    ls_toolbar-function    = cl_gui_alv_grid=>mc_fc_loc_undo.
    ls_toolbar-quickinfo  = text-052"Rckgngig
    ls_toolbar-icon        = icon_system_undo.
    ls_toolbar-disabled    = space.
    APPEND ls_toolbar TO mt_toolbar.

*... Toolbar Separator
    CLEAR ls_toolbar.
    ls_toolbar-function    = '&&SEP02'.
    ls_toolbar-butn_type  = 3.
    APPEND ls_toolbar TO mt_toolbar.

*... Toolbar Button APPEND ROW
    CLEAR ls_toolbar.
    ls_toolbar-function    = cl_gui_alv_grid=>mc_fc_loc_append_row.
    ls_toolbar-quickinfo  = text-054"Zeile anhngen
    ls_toolbar-icon        = icon_create.
    ls_toolbar-disabled    = space.
    APPEND ls_toolbar TO mt_toolbar.

*... Toolbar Button INSERT ROW
    CLEAR ls_toolbar.
    ls_toolbar-function    = cl_gui_alv_grid=>mc_fc_loc_insert_row.
    ls_toolbar-quickinfo  = text-048"Zeile einfgen
    ls_toolbar-icon        = icon_insert_row.
    ls_toolbar-disabled    = space.
    APPEND ls_toolbar TO mt_toolbar.

*... Toolbar Button DELETE ROW
    CLEAR ls_toolbar.
    ls_toolbar-function    = cl_gui_alv_grid=>mc_fc_loc_delete_row.
    ls_toolbar-quickinfo  = text-049"Zeile lschen
    ls_toolbar-icon        = icon_delete_row.
    ls_toolbar-disabled    = space.
    APPEND ls_toolbar TO mt_toolbar.

*... Toolbar Button COPY ROW
    CLEAR ls_toolbar.
    ls_toolbar-function    = cl_gui_alv_grid=>mc_fc_loc_copy_row.
    ls_toolbar-quickinfo  = text-051"Duplizieren
    ls_toolbar-icon        = icon_copy_object.
    ls_toolbar-disabled    = space.
    APPEND ls_toolbar TO mt_toolbar.

*... Toolbar Separator
    CLEAR ls_toolbar.
    ls_toolbar-function    = '&&SEP03'.
    ls_toolbar-butn_type  = 3.
    APPEND ls_toolbar TO mt_toolbar.

    APPEND LINES OF mt_toolbar TO e_object->mt_toolbar.

  ENDMETHOD.                    "on_toolbar
ENDCLASS.                    "lcl_event_handler IMPLEMENTATION

 


From time to time the issue of text-symbols usage keeps popping-up in my daily work, so I decided to write a short blog about why I think it is better to avoid using text-symbols as literals in your code.

You have a report / function module / class where you need to use a text that has to be translatable. One way of doing this is to define a text-symbol.

 

Now the tricky part is that you can define / use a text-symbol in two ways and it will behave differently when you want to change it:

  1. You can create a text-symbol by using Goto --> Text elements and reference it in your code via text-ccc (E.g.: text-001) OR
  2. You can create a literal, reference the text-symbol via a 3-characters ID and use forward navigation (double click on it) to create the text-symbol (E.g.: l_string = ‘Hello world!’(001))

 

When you choose the second option to create and reference a text symbol, keep in mind the followings:

  • If you modify the literal, you always need to use forward navigation to transfer the new value into the text-symbol. Otherwise, the value in use will be the old one.

    E.g.: You change
    l_string = ‘Hello world!’(001) into
    l_string = ‘Hello ABAP!’(001) and you forget to use forward navigation to replace the text-symbol's old value with the new one.
    If you output l_string’s value you will see it’s actually ‘Hello world!’ instead of what you might have expected, that is ‘Hello ABAP!’.

  • If you modify the text-symbols via Goto --> Text elements, the text-symbol will have a value which differs from the literal used in your code. The value that is actually in use is the one from the text-symbol.

    E.g.: You go to Goto --> Text elements and you change the value of the text-symbol 001 from ‘Hello world!’ to ‘Hello ABAP!’. In your code, you are still using l_string = ‘Hello world!’(001).
    If you output l_string’s value you will see it is ‘Hello ABAP!’ which, at a first glance, might seem awkward because in your code you have ‘Hello world!’.



Therefore, in order to avoid a mismatch between the actual value in use (which is always the text-symbol) and the value of the literal, reference text-symbols as text-ccc in your code.

Actions

Filter Blog

By author:
By date:
By tag: