Quantcast
Channel: ABAP Development
Viewing all 948 articles
Browse latest View live

SAP Business Intelligence with Elasticseach and Kibana

$
0
0

After having played around with Elasticsearch for indexing PI payloads and indexing ABAP source code, and especially after seeing Kibana 4, it occurred to me that Elasticsearch would actually be great for business intelligence reporting.

 

As a proof of concept, I wrote a little extraction program (see below for a link to the source) to read sales data from a development system and export it to Elasticseach. It is very rudimentary and you should of course read the warnings (because it is only a proof of concept after all).

 

What this demonstrates, however, is how quick and easy it is to do this kind of reporting in Elasticsearch, and in fact to extract the data from SAP. (Elasticsearch really requires zero configuration to get going, as opposed to Apache Solr, which I have used in the past). There are of course many areas for improvement in this solution, one of the important ones being the need to configure the index upfront to set certain fields not to be analyzed. This would be particularly true for the customer name, if you wanted to use that for reporting, or add material description instead of using the code.

 

Note that it is very tempting to want to normalize the data. There might be valid reasons for that: for example, you may want to change the name of a customer or description of a material on SAP at some stage. However, just for the sake of saving a few MiB of disk space it is really not worth the effort, and you wouldn’t gain any performance advantage from it anyway.

 

Nonetheless, just to test it out, I set up two little queries with Kibana 4 and made them into a little dashboard showing the value of sales per month and the value of sales by the top 5 materials.

 

http://ceronio.net/wp-content/uploads/2015/02/2015-02-20-09_40_32-Dashboard-Kibana-4.png

 

Kibana gives you some very nifty”slice-and-dice” and “drill-down” features. For example, you can select a value (like a slice of the pie chart) or drag-select a certain period of time on the above dashboard, and it will automatically filter the data by that selection.

 

On the other hand, Kibana gives you rather limited functionality in terms of visualizing the data (e.g. setting labels on pie chart slices does not seem possible) which your users are probably going to demand, but for that you have many options such as building a dashboard with the likes of the D3 javascript library or so. (In fact, there is an Elasticsearch blog post on just this topic). The possibilities are virtually limitless and, with a little elbow grease, you can come up with some great reports.

 

The source for the extract program, if you are interested, is here: https://gist.github.com/mydoghasworms/1beaf4b8d013fe7ee95a. To test it out, you will need an ECC system with some sales data, and a running Elasticsearch server, which you can easily set up by extracting the downloaded file and installing the service. You will also need to install Kibana 4, which (as opposed to the old Kibana, which ran as a plugin on the Elasticsearch server). If you need help setting it up, please drop me a comment below!

 

(This article has been adapted from the original here).


Boxed Data - A Dynamic ABAP Programming Framework

$
0
0

Hi Everyone. It has been a long time coming but I am finally posting probably the last custom object framework for a while / ever. This framework was completed back in 2014 for a specific scenario (to create an awesome proxy layer that can call any SPROXY service with dynamic results) and so I am now finally sharing this. If you ever had to create an object with a data ref as an instance variable then create setters and getters for that reference then you might want to take a look at this.


Please take note that i have just copied an old document here and I have not bothered to update it, this is because you can find the most up to date data on the README page of my Git-hub, which is also coincidentally the location you must go to if you want to get the .nugg file which contains all the objects required to use this framework.


Finally the link to Git-hub is here:

lessonteacher/boxed_data · GitHub

 

And as mentioned i would recommend reading that document instead of this as I have not edited this one further. Hope you enjoy reading and maybe even find the ideas useful.


Boxed Data - Dynamic Programming Wrapper Objects
Note: This page uses syntax provided by the ABAP 7.4 release, for info on that check out the following page ABAP Language News for Release 7.40


The 'Boxed Data Framework' is a set of ABAP objects which are designed to abstract and provide usability for commonly used dynamic programming scenarios in ABAP. The framework provides a number of useful capabilities that can assist with manipulating ABAP types in a generic fashion. This document will provide a very short introduction and then immediately describe the usage.

 

Introduction

There are a couple of key scenarios where the framework comes in handy. To start, lets imagine the case where we want to have a structure which is stored as an object. This is a relatively common case, the structure itself is abstracted away in the object and it has 'set_attribute(name,value)' or 'get_attribute(name):value' methods. This might then form the base of another set of objects which use this object to store the data internally. Of course the underlying structure can differ per object but the access is always the same through the provided methods.


This pattern is often implemented to allow abstraction of some kind of object or 'Entity' with its own structure that can be used the same way every time. In order to eliminate the requirement to create such a structure, the BoxedStructure was created. The boxed data framework allows for abstraction of any ABAP type. For complex types, the framework provides abstraction of not only the type but also the nested access or dynamic data access in a flexible way which would otherwise be quite irritating.


The following sketch shows the layout of the framework:

BoxedData ~ the base abstract boxed type

            BoxedComplexType ~ abstract complex type

BoxedStruct~ structures

BoxedTableset ~ table types

            BoxedElement~ elements like strings, ints etc

            BoxedRef ~ data references (limited use cases)

            BoxedObject~ object references (limited use cases)

BoxPath ~ path for accessing complex data

BoxedPacker ~ packs boxed types using the box() method



Usage - Elements / Basics

The following section will just work through the usages of each boxed type. The entire framework can be found in the ZDATA_WRAPPING package currently. Basic elements such as strings etc will be boxed into a type of zcl_boxed_element.

A boxed type can be constructed as a normal object(note the use of # is abap 7.4)

data: lr_box type ref to zcl_boxed_element. "... These are 'elements'
lr_box
= new #( 'This is a string'  ). "... String
lr_box
= new #( 123  ). "... Int


If the type is not known, the zcl_boxed_packer=>box() method should be used

data: lr_box type ref to zcl_boxed_data. "... The base type
lr_box
= zcl_boxed_packer=>box( 'This is a string' ). "... Using the packer


Of course the types can be boxed directly from the variable.

data: lv_str type string value 'This is a string',
lv_int
type i value 123.

data(lr_str) = zcl_boxed_packer=>box( lv_str ). "... Use the packer!
data(lr_int) = zcl_boxed_packer=>box( lv_int ).

Note that this will create a copy of the data to box it which is then subsequently accessed through the reference in the object. The meaning of this is that the object will change but the originating variable will remain unchanged.

data: lv_str type string value 'This is not changed'.

data(lr_str) = zcl_boxed_packer=>box( lv_str ).
lr_str
->set_value( 'but this is a new value' ).


Printing the values shows the output, note that the boxed data objects have a 'to_string()' method, although some output is a little more useful than others

write lv_str. new-line.
write lr_str->to_string( ).

This is not changed

but this is a new value

 

Finally to illustrate by example, this allows types to be boxed and used in a dynamic way might otherwise be annoying.

data: lv_str type string value 'This is a string',
lv_int
type i value 123,
lv_date
type dats value '20140501',
lv_p
type p decimals 2 value '123.23',
lt_boxed
type standard table of ref to zcl_boxed_element. "... Table of elements

"... Append all the elements(usually use the boxed packer)
append new #( lv_str ) to lt_boxed.
append new #( lv_int ) to lt_boxed.
append new #( lv_date ) to lt_boxed.
append new #( lv_p ) to lt_boxed.

"... Write them out
loop at lt_boxed into data(lr_box).
write lr_box->to_string( ). new-line.
endloop.

This is a string

123

20140501

123.23


The base boxed data object provides common functionality to set and get the values. Note that the types can always be exported to their relevant type or a type of data ref can be returned. In all cases when setting a value the types must be convertable between each other. If the types dont match the set_value() or get_value() methods will always fail.

data: lv_str type string,
lv_int
type i,
lr_data
type ref to data.

data(lr_str) = zcl_boxed_packer=>box( lv_str ).

lr_str
->set_value( 'This is a string' ). "... Sets a new value
lr_str
->get_value( importing value = lv_str ). "... The types must match!
lr_data
= lr_str->get_data( ). "... A data ref can be pulled out
*lr_str->get_value( importing value = lv_int ). "... This will fail! (the int cant be passed here!)



Usage - Structures

The boxed class for working with structures is zcl_boxed_struct. This class is a subclass of the boxed data base class and contains all the above functionality, however, it also has some more methods specific to structures.

For the purpose of this section the following will be the example structure. It is an address structure with a phone structure inside that.

types:
begin of phone_line,
number type i,
area
type string,
end of phone_line,
begin of address_line,
number type i,
street
type string,
state
type char3,
postcode
type string,
phone
type phone_line,
end of address_line.


Once again, the boxed type can be constructed, or it can be packed using the boxed packer. The usual case is the use of the packer as often the types are unknown since that is the real purpose of this framework, however, it is always possible to construct the object directly.

data: ls_address type address_line,
lr_box
type ref to zcl_boxed_struct. "... The structure is typed here

"... Create the address, forget the phone for now
ls_address
-number = 23.
ls_address
-street = 'Awesome Street'.
ls_address
-state = 'SA'.
ls_address
-postcode = '5000'.

"... Box this
lr_box
= new #( ls_address ). "... Create struct
lr_box ?= zcl_boxed_packer
=>box( ls_address ). "... Packer can / should be used


Notice that a cast is used when boxing the data with the packer, this is often required to allow access to the structures additional methods. Such as, the set_attribute() method.

"... Box this
lr_box
= new #( ls_address ). "... Create struct
lr_box
->set_attribute( name = 'number' value = 28 ).

 

The get_attribute() method returns an attribute as a boxed data type so in the example below, the to_string() method is available.

write lr_box->get_attribute( 'number' )->to_string( ).

28

 

Both the set_attributes() and get_attributes() methods perform a move-corresponding statement.

lr_box->set_attributes( ls_address ).
lr_box
->get_attributes( importing attributes = ls_address ). ".. Moves corresponding!

 

Of course it is possible to use the base boxed data get_value() method, which would have actually worked in the above example in place of the get_attributes( ) method, but it is important to understand the distinction. The get_attributes method will move to a structure with corresponding field names whereas the get_value() method shown below requires the exact address_line type.

lr_box->get_value( importing value = ls_address ). "... req. correct type


Note that we have not used the phone structure yet. Working with this structure when embedded in another structure will definitely be more painful than using the basic syntax e.g. 'ls_address-phone-number'. But if you can imagine, if we did not know the types at compile time, this would be an unenjoyable process of 'assign component 'phone' of structure ls_address to <fs_phone>'. Subsequently another assignment would then be needed to 'assign component 'number' of structure <fs_phone> to <fs>'. That is the real power of the Boxed Data Framework. Lets see the comparisons together, notice that we will introduce the resolve_path() method here.

"... If the types are known then obviously a structure is fine to work with
ls_address
-phone-number = 1234. "... Set the structure
lr_box
->resolve_path( 'phone-number' )->set_value( 1234 ). "... Set the boxed value


When the type is known there would be no significant value, other than for generic type access, to use a boxed struct here but we see that the path can be expressed using the zcl_box_path syntax in the resolve_path() method. If we imagine that we dont know the actual type(although we do in this case) then with dynamic programming the standard syntax can be punishing (7.4 makes it actually a little neater than before with the inline field-symbol() declaration).

"... When the types are unknown at compile time the dynamic syntax, is not ideal
assign component 'phone' of structure ls_address to field-symbol(<fs_phone>).
assign component 'number' of structure <fs_phone> to field-symbol(<fs_number>).
<fs_number>
= 1234. "... Set the value
lr_box
->resolve_path( 'phone-number' )->set_value( 1234 ). "... The boxed syntax doesnt change


As seen, the resolve_path() method can provide access to the attributes, however, note that the return of this method is a type of the abstract class zcl_boxed_data it is then possible to perform a cast if additional functions of the nested boxed type are required.

"... Cast into the boxed struct
data(lr_phone) = cast zcl_boxed_struct(lr_box->resolve_path( 'phone' ) ).
lr_phone
->set_attribute( name = 'number' value = 1234 ). "... Set the attribute


Usage - Table Types

Table types can be boxed and obviously that means interacting with them will require some special functionality.The boxed type that is created when working with table types is zcl_boxed_tableset. Working with these objects mean that there are some relative limitations imposed by the framework. For example it is easy natively to work with ABAP table types whereas you need to work through the provided objects such as the zif_boxed_iterator to iterate over a collection of boxed data. Of course the underlying representation of the contents of the boxed tableset is a generic table, which means that key access is preferred, in fact... index access is not provided out of the box for ABAP dynamic tables. It is provided by the framework, however, but it must be understood that indexed retrievals are not as efficient as key retrievals and in some cases where a hashed or sorted table has been boxed an index retrieval could return quite a confusing result.

We will continue to use the structure provided in the above structure examples in order to show the boxing of tables. The below example has a table which is already loaded with addresses.

data: ls_address type address_line,
lt_addresses
type standard table of address_line, "... Table of addresses
lr_table
type ref to zcl_boxed_tableset. "... Tableset here

"... Create some addresses (without phones)
ls_address
-number = 11.
ls_address
-street = 'Awesome Street'.
ls_address
-state = 'SA'.
ls_address
-postcode = '5000'.
append ls_address to lt_addresses.

ls_address
-number = 22.
ls_address
-street = 'New Thing Street'.
ls_address
-state = 'NSW'.
ls_address
-postcode = '1201'.
append ls_address to lt_addresses.

ls_address
-number = 33.
ls_address
-street = 'Other  Street'.
ls_address
-state = 'ACT'.
ls_address
-postcode = '2600'.
append ls_address to lt_addresses.


The boxed tableset objects are created the same way the structures were created in the previous section, either using the tableset object constructor or by using the zcl_boxed_packer object

lr_table = new #( lt_addresses ). "... Create object
lr_table ?= zcl_boxed_packer
=>box( lt_addresses ). "... Use the packer (and cast)


Looping on a table requires the use of the iterator and its various functions

lr_table = new #( lt_addresses ). "... Create object

data(lr_iterator) = lr_table->iterator( ). "... Always creates a new iterator

while lr_iterator->has_next( ) eq abap_true.
data(lr_line) = cast zcl_boxed_struct( lr_iterator->next( ) ). "... Requires cast

write lr_line->get_attribute( 'street' )->to_string( ). new-line.
endwhile.

Note: a cast is required because the next() method returns a boxed data object not a boxed structure. Of course the contents of the table does not have to be structures as line types, so the generic result is required

An alternative is the use of the find() statement which will find and return the first matching result. The condition must be a valid ABAP dynamic table condition, however, the '|' symbols may be used in place of the ''' symbol as it would need to be escaped (though you can use it if you wish).

data: lr_struct type ref to zcl_boxed_struct. "... Declare structure
lr_struct ?= lr_table
->find( 'state eq |NSW|' ). "... Find the address in NSW


If more than one result is expected, the find_all() method can be used. It will return a zcl_boxed_tableset object.

data: lr_result type ref to zcl_boxed_tableset. "... The results
lr_result ?= lr_table
->find_all( 'postcode cp |*00|' ). "... Find postcodes ending in 00

data(lv_str) = lr_result->to_string( ).

...

lv_str->

number:11
street:Awesome Street
state:SA
postcode:5000
phone:number:0
area:

 

number:33
street:Other  Street
state:ACT
postcode:2600
phone:number:0
area:


Of course we may need to delete entries from our table, the remove() and remove_value() methods perform this function. In either case the underlying line type is used for the deletion, so where remove() takes a boxed data object the remove_value() takes a structure or other type which represents the line type of the table object.

...

ls_address-number = 33.
ls_address
-street = 'Other  Street'.
ls_address
-state = 'ACT'.
ls_address
-postcode = '2600'.
append ls_address to lt_addresses.

lr_table
= new #( lt_addresses ). "... Create object

lr_table
->remove_value( ls_address ). "... The last address is removed
lr_table
->remove( lr_table->find( 'state = |SA|' ) ). ".. The first address is removed here

It would be relatively uninteresting if we couldnt find an item by index, so the following example removes index 2.

lr_table = new #( lt_addresses ). "... Create object

lr_table
->remove( lr_table->find( '[2]' ) ). "... Remove index 2


It is possible to insert() a boxed data object or use insert_value() to insert a structure to a tableset. The following example inserts the structure value, note that currently it either appends or inserts depending on the table type, ie hashed, sorted or standard table.

lr_table = new #( lt_addresses ). "... Create object with empty table

"... Create some addresses (without phones)
ls_address
-number = 11.
ls_address
-street = 'Awesome Street'.
ls_address
-state = 'SA'.
ls_address
-postcode = '5000'.

lr_table
->insert_value( ls_address ). "... Insert the address structure


A boxed structure can be inserted with the insert() method, the following example is identical to the above except that it boxes the structure then inserts it.

"... Create some addresses (without phones)
ls_address
-number = 11.
ls_address
-street = 'Awesome Street'.
ls_address
-state = 'SA'.
ls_address
-postcode = '5000'.

lr_table
->insert( zcl_boxed_packer=>box( ls_address ) ). "... Insert the boxed address structure


Considering the examples provided, it is evident that there is a requirement to be able to get the line type of a table. The get_line() method will return an empty boxed data object that can be used. The below example does this, it requires a cast once again as the line type can be any type in a tableset.

data: lr_address type ref to zcl_boxed_struct, "... Now this is the structure
lr_table
type ref to zcl_boxed_tableset, "... Tableset for the addresses
lt_addresses
type standard table of address_line. "... Table type

lr_table
= new #( lt_addresses ). "... Create object with empty table

lr_address ?= lr_table
->get_line( ). "... Get the line out

lr_address
->set_attribute( name = 'number' value = 11 ). "... Set the number
lr_address
->set_attribute( name = 'street' value = 'Awesome Street' ).
lr_address
->get_attribute( 'state' )->set_value( 'SA' ). "... This is equivalent to above!
lr_address
->set_attribute( name = 'postcode' value = '5000' ).

lr_table
->insert( lr_address ). "... Insert the address


Finally, in the event that the table needs to be cleared, the method clear() is provided.

lr_table->clear( ). "... Clears the values

 

Usage - Path Resolution
In order to provide even greater access to complex types, and even to be able to generically access dynamic attributes within complex types like structures, the Box Path object and relative syntax was created. Using the resolve_path() method, as seen previously, attributes can be accessed from deeply nested structures, tables and in some cases attributes can be retrieved using the wildcard '*-' prefix.

For the purposes of the discussion the following structure will be used.

types:
begin of phone_line,
number type n length 7,
area
type string,
end of phone_line,
phone_tab
type standard table of phone_line with default key,
begin of address_line,
number type i,
street
type string,
state
type char3,
postcode
type string,
phone
type phone_tab, "... Now this is a table
end of address_line,
begin of customer,
first_name
type string,
last_name
type string,
age
type i,
primary_address
type address_line,
end of customer.


The structure is then populated in the following code.

data: ls_phone type phone_line,
ls_customer
type customer.

"... Setup the customer structure
ls_customer
-first_name = 'Bill'.
ls_customer
-last_name = 'Smith'.
ls_customer
-age = 30.
ls_customer
-primary_address-number = 62.
ls_customer
-primary_address-postcode = '5000'.
ls_customer
-primary_address-state = 'SA'.
ls_customer
-primary_address-street = 'Awesome Street'.

ls_phone
-area = '08'.
ls_phone
-number = 8432345.
append ls_phone to ls_customer-primary_address-phone.

ls_phone
-area = '08'.
ls_phone
-number = 8464324.
append ls_phone to ls_customer-primary_address-phone.

ls_phone
-area = '042'.
ls_phone
-number = 6423423.
append ls_phone to ls_customer-primary_address-phone.


Data can be retrieved using the resolve_path() statement from nested structures. The same syntax is provided as would be seen when accessing the data in ABAP.

data(lr_box) = zcl_boxed_packer=>box( ls_customer ). "... Box the structure

data(lr_state) = lr_box->resolve_path( 'primary_address-state' ). "... Get the state


Dynamic where conditions can be added to find data that is within tables. Note that the first match is returned and the search for the relevant attributes occurs via a breadth first search.

data(lr_box) = zcl_boxed_packer=>box( ls_customer ). "... Box the structure

"... Get the phone number of the first phone entry
data(lr_num) = lr_box->resolve_path( 'primary_address-phone[1]-number' ).


It is also possible to do a wildcard search by using the '*-' prefix, note that if a table is empty this would not return a value. If the result is not found the result will be not bound.

data(lr_box) = zcl_boxed_packer=>box( ls_customer ). "... Box the structure

"... Get the first field called 'number'
data(lr_num) = lr_box->resolve_path( '*-number' ).

write lr_num->to_string( ).

...

62


Note that the above returns the first street number! this is because during the BFS the first field found is the 'number' in the address_line structure. It is important to understand this distinction. To resolve, demonstrating the wildcard further, the path could be extended to apply a more specific relation.

data(lr_box) = zcl_boxed_packer=>box( ls_customer ). "... Box the structure

"... Get the first phone entry's number
data(lr_num) = lr_box->resolve_path( '*-phone[1]-number' ).

write lr_num->to_string( ).

...

8432345


The following is a final example.

data(lr_box) = zcl_boxed_packer=>box( ls_customer ). "... Box the structure

"... Get the first phone number where the area is '042'
data(lr_num) = lr_box->resolve_path( '*-phone[area eq |042|]-number' ).

write lr_num->to_string( ).

...

6423423

 

Usage - Mappings and Bindings*

Mappings are currently implemented. Unfortunately I have no examples here to provide. Bindings were an upcomming feature that likely will not ever be complete.

Custom program to Convert Amount from one currency to Another currency

$
0
0

Custom program to Convert Amount from one currency to another currency

As most of the technical consultants know that how to convert the amount from one currency to another currency.

 

But in this blog I would like to give you a brief overview of calculating accurate Converted amount and Exchange rate values. I Hope It may useful for you while ABAP Coding.


Prerequisites

  • Basic knowledge on ABAP.

Technical code explanation:

  1. Initially the input file must contain four mandatory parameters

    • Conversion Date
    • From Currency
    • From Amount
    • To Currency (To what currency we would like to convert the input amount)

      2. Output file structure

      • Conversion Date
      • From Currency
      • From Amount
      • To Currency
      • To Amount
      • Exchange Rate

       

        3. Read data from the input excel file using FM: TEXT_CONVERT_XLS_TO_SAP.

        4. Loop over all the input file records and pass the relevant input values to the standard SAP FM: CONVERT_TO_LOCAL_CURRENCY and consider only          the below output parameters:

        • Exchange rate
        • Foreign factor
        • Local factor

         

        Note: Don’t consider the Converted amount (Local amount) directly from the FM: CONVERT_TO_LOCAL_CURRENCY. Instead there was a small logic to calculate the actual accurate CONVERTED AMOUNT by following the below logic:

         

        *-Below is the logic to determine the Exact values of Conv. amt and Exc. Rate
        IF lv_excrate IS NOT INITIAL.
        IF lv_excrate GE 0.
        lv_excrate_actual
        = lv_excrate / ( foreign / locfact ).
        ELSE.
        lv_excrate
        = lv_excrate * -1.
        lv_excrate_actual
        = ( 1 / lv_excrate ) / ( foreign / locfact ).
        ENDIF.

        IF lv_excrate_actual NE 0.
        converted_amt_actual
        = lr_ipfile->from_amt * lv_excrate_actual.
        ENDIF.
        ENDIF.

         

          5. The above logic is used to calculate accurate Converted Amount and Exchange rate values even the exchange rate was maintained in either of the                below blocks and even the input amount is Negative.

         

        1.jpg

         

          6. Technical coding part is as shown below, copy and paste the below logic in your systems and try to see the acurate results.


        REPORT  zrhramtconversion.

        TYPE-POOLS: slis,truxs.
        *-Structure declarations
        TYPES: BEGIN OF ty_ipfile,
        conv_date
        TYPE char8,
        from_curr
        TYPE fcurr_curr,
        from_amt
        TYPE dec11_4,
        to_curr
        TYPE tcurr_curr,
        to_amt
        TYPE maxbt,
        exc_rate
        TYPE p DECIMALS 5,
        message TYPE char255,
        END OF ty_ipfile.

        DATA:BEGIN OF lt_title OCCURS 2,
        title(200),
        END OF lt_title.

        *-Declarations
        DATA: lv_outfile TYPE string,
        lv_msg
        TYPE string.

        DATA: lt_ipfile TYPE TABLE OF ty_ipfile,
        lv_date
        TYPE datum,
        lt_outfile
        TYPE TABLE OF ty_ipfile.

        DATA: lr_ipfile TYPE REF TO ty_ipfile.

        DATA: converted_amt_actual TYPE p DECIMALS 3,
        lv_amount
        TYPE p DECIMALS 3.

        DATA: lv_excrate TYPE tkurs,
        lv_excrate_actual
        TYPE ckrate,
        foreign
        TYPE i,
        locfact
        TYPE i,
        excratex
        (8) TYPE p,
        fixrate
        TYPE tcurr-ukurs,
        derive
        TYPE tcurr-kurst.

        *-Selection screen
        SELECTION-SCREEN BEGIN OF BLOCK rad1
        WITH FRAME TITLE title.

        PARAMETERSp_ipf    LIKE rlgrap-filename OBLIGATORY"Input File Path
        p_outfl 
        AS CHECKBOX USER-COMMAND c1,    "Checkbox for output file
        p_outf   
        TYPE string MODIF ID id1.        "Output File Path

        SELECTION-SCREEN END OF BLOCK rad1.

        INITIALIZATION.
        title = text-001.

        AT SELECTION-SCREEN OUTPUT.
        LOOP AT SCREEN.
        IF p_outfl <> 'X'.
        CLEAR p_outf.
        IF screen-group1  = 'ID1' .
        screen-invisible = '1'.
        screen-input    = '0'.
        screen-output    = '0'.
        ENDIF.
        MODIFY SCREEN.
        ENDIF.
        ENDLOOP.

        *-Input file F4 Functionality
        AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_ipf.
        PERFORM fetch_file CHANGING p_ipf.

        *-Directory browse for Output file
        AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_outf.
        *-F4 help for getting directory in which output file should be downloaded
        CALL METHOD cl_gui_frontend_services=>directory_browse
        CHANGING
        selected_folder     
        = p_outf
        EXCEPTIONS
        cntl_error         
        = 1
        error_no_gui       
        = 2
        not_supported_by_gui
        = 3
        OTHERS              = 4.
        IF sy-subrc <> 0.
        MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
        WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.

        START-OF-SELECTION.

        IF p_ipf IS NOT INITIAL.
        *-Read data from XLS to Internal Table
        PERFORM read_file TABLES lt_ipfile USING p_ipf.
        ELSE.
        MESSAGE text-002 TYPE 'E'.
        ENDIF.

        *-Loop Input file, Calculate the converted amounts and
        *                                Fill the Output file
        LOOP AT lt_ipfile REFERENCE INTO lr_ipfile.
        CLEAR lv_date.
        lv_date
        = lr_ipfile->conv_date.
        CLEAR: sy-msgty,sy-msgid,sy-msgno,
        sy
        -msgv1,sy-msgv2,sy-msgv3,sy-msgv4.

        CALL FUNCTION 'CONVERT_TO_LOCAL_CURRENCY'
        EXPORTING
        date              = lv_date
        foreign_amount   
        = lr_ipfile->from_amt
        foreign_currency 
        = lr_ipfile->from_curr
        local_currency   
        = lr_ipfile->to_curr
        rate             
        = 0
        type_of_rate     
        = 'M'
        read_tcurr       
        = 'X'
        IMPORTING
        exchange_rate   
        = lv_excrate
        foreign_factor   
        = foreign
        local_amount     
        = lv_amount
        local_factor     
        = locfact
        exchange_ratex   
        = excratex
        fixed_rate       
        = fixrate
        derived_rate_type
        = derive
        EXCEPTIONS
        no_rate_found   
        = 1
        overflow         
        = 2
        no_factors_found 
        = 3
        no_spread_found 
        = 4
        derived_2_times 
        = 5
        OTHERS            = 6.

        IF sy-subrc = 0.
        lr_ipfile
        ->message = text-011.

        *-Below is the logic to determine the Exact values of Conv. amt and Exc. Rate
        IF lv_excrate IS NOT INITIAL.
        IF lv_excrate GE 0.
        lv_excrate_actual
        = lv_excrate / ( foreign / locfact ).
        ELSE.
        lv_excrate
        = lv_excrate * -1.
        lv_excrate_actual
        = ( 1 / lv_excrate ) / ( foreign / locfact ).
        ENDIF.

        IF lv_excrate_actual NE 0.
        converted_amt_actual
        = lr_ipfile->from_amt * lv_excrate_actual.
        ENDIF.
        ENDIF.

        lr_ipfile
        ->to_amt = converted_amt_actual.
        lr_ipfile
        ->exc_rate = lv_excrate_actual.

        ELSE.

        IF lv_date IS INITIAL OR
        lr_ipfile
        ->from_amt IS INITIAL OR
        lr_ipfile
        ->from_curr IS INITIAL OR
        lr_ipfile
        ->to_curr IS INITIAL.
        CONCATENATE text-012 text-013 INTO lr_ipfile->message
        SEPARATED BY space.
        ELSE.

        *-Formatting the message to a required format
        CALL FUNCTION 'FORMAT_MESSAGE'
        EXPORTING
        id        = sy-msgid
        lang     
        = sy-langu
        no        = sy-msgno
        v1       
        = sy-msgv1
        v2       
        = sy-msgv2
        v3       
        = sy-msgv3
        v4       
        = sy-msgv4
        IMPORTING
        msg     
        = lv_msg
        EXCEPTIONS
        not_found
        = 1
        OTHERS    = 2.
        IF sy-subrc <> 0.
        *        MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
        *                        WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.

        *-Filling Error message to the Output messages
        IF lv_msg IS NOT INITIAL.
        CONCATENATE text-012 lv_msg INTO lr_ipfile->message
        SEPARATED BY space.
        ENDIF.
        ENDIF.
        ENDIF.

        CLEAR: lv_excrate_actual,converted_amt_actual,
        lv_excrate
        ,foreign,locfact,lv_msg.
        ENDLOOP.

        END-OF-SELECTION.
        *-Assign updated input file to output file
        lt_outfile[]
        = lt_ipfile[].
        IF p_outf IS NOT INITIAL.
        *-Output File path
        CONCATENATE p_outf text-003 sy-datum sy-uzeit '.xls' INTO lv_outfile.
        *-Fill Header for the Output file
        PERFORM file_header .
        *-Download the Output to Output File
        PERFORM download.
        ENDIF.

        *-ALV Grid Output
        PERFORM display.

        *&---------------------------------------------------------------------*
        *&      Form  FETCH_FILE
        *&---------------------------------------------------------------------*
        *      text
        *----------------------------------------------------------------------*
        *      <--P_P_IPF  text
        *----------------------------------------------------------------------*
        FORM fetch_file  CHANGING p_p_ipf.
        CALL FUNCTION 'F4_FILENAME'
        IMPORTING
        file_name
        = p_p_ipf.
        ENDFORM.                    " FETCH_FILE
        *&---------------------------------------------------------------------*
        *&      Form  READ_FILE
        *&---------------------------------------------------------------------*
        *      text
        *----------------------------------------------------------------------*
        *      -->P_LT_GRANT  text
        *      -->P_P_IPF  text
        *----------------------------------------------------------------------*
        FORM read_file  TABLES  p_lt_ipfile USING  p_ipf.

        DATA: ls_truxs TYPE truxs_t_text_data.

        CALL FUNCTION 'TEXT_CONVERT_XLS_TO_SAP'
        EXPORTING
        i_line_header       
        = 'X'
        i_tab_raw_data     
        = ls_truxs
        i_filename         
        = p_ipf
        TABLES
        i_tab_converted_data
        = p_lt_ipfile
        EXCEPTIONS
        conversion_failed   
        = 1
        OTHERS              = 2.

        IF sy-subrc <> 0.
        MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
        WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.

        ENDFORM.                    " READ_FILE
        *&---------------------------------------------------------------------*
        *&      Form  FILE_HEADER
        *&---------------------------------------------------------------------*
        *      text
        *----------------------------------------------------------------------*
        *  -->  p1        text
        *  <--  p2        text
        *----------------------------------------------------------------------*
        FORM file_header.
        lt_title
        -title = text-004.
        APPEND lt_title.
        lt_title
        -title = text-005.
        APPEND lt_title.
        lt_title
        -title = text-006.
        APPEND lt_title.
        lt_title
        -title = text-007.
        APPEND lt_title.
        lt_title
        -title = text-008.
        APPEND lt_title.
        lt_title
        -title = text-009.
        APPEND lt_title.
        lt_title
        -title = text-010.
        APPEND lt_title.
        ENDFORM.                    " FILE_HEADER
        *&---------------------------------------------------------------------*
        *&      Form  DOWNLOAD
        *&---------------------------------------------------------------------*
        *      text
        *----------------------------------------------------------------------*
        *  -->  p1        text
        *  <--  p2        text
        *----------------------------------------------------------------------*
        FORM download.

        CALL METHOD cl_gui_frontend_services=>gui_download
        EXPORTING
        filename               
        = lv_outfile
        filetype               
        = 'DBF'
        write_field_separator 
        = 'X'
        fieldnames             
        = lt_title[]
        CHANGING
        data_tab               
        = lt_outfile[]
        EXCEPTIONS
        file_write_error       
        = 1
        no_batch               
        = 2
        gui_refuse_filetransfer
        = 3
        invalid_type           
        = 4
        no_authority           
        = 5
        unknown_error         
        = 6
        header_not_allowed     
        = 7
        separator_not_allowed 
        = 8
        filesize_not_allowed   
        = 9
        header_too_long       
        = 10
        dp_error_create       
        = 11
        dp_error_send         
        = 12
        dp_error_write         
        = 13
        unknown_dp_error       
        = 14
        access_denied         
        = 15
        dp_out_of_memory       
        = 16
        disk_full             
        = 17
        dp_timeout             
        = 18
        file_not_found         
        = 19
        dataprovider_exception 
        = 20
        control_flush_error   
        = 21
        not_supported_by_gui   
        = 22
        error_no_gui           
        = 23
        OTHERS                  = 24.
        IF sy-subrc <> 0.
        MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
        WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.

        ENDFORM.                    " DOWNLOAD
        *&---------------------------------------------------------------------*
        *&      Form  DISPLAY
        *&---------------------------------------------------------------------*
        *      text
        *----------------------------------------------------------------------*
        *  -->  p1        text
        *  <--  p2        text
        *----------------------------------------------------------------------*
        FORM display.

        *-Declaration for ALV Grid
        DATA : gr_table TYPE REF TO cl_salv_table.
        DATA : gr_functions TYPE REF TO cl_salv_functions_list.
        DATA : gr_columns TYPE REF TO cl_salv_columns_table,
        gr_column 
        TYPE REF TO cl_salv_column_table.

        CLEAR : gr_table.
        TRY.
        CALL METHOD cl_salv_table=>factory
        EXPORTING
        list_display
        = if_salv_c_bool_sap=>false
        IMPORTING
        r_salv_table
        = gr_table
        CHANGING
        t_table     
        = lt_outfile.
        CATCH cx_salv_msg.                "#EC NO_HANDLER
        ENDTRY.

        IF gr_table IS BOUND.
        *-Get functions
        gr_functions
        = gr_table->get_functions( ).

        *-Enable Buttons in Tool Bar
        gr_functions
        ->set_all( if_salv_c_bool_sap=>true ).

        *-Get the columns from table
        gr_columns
        = gr_table->get_columns( ).
        IF gr_columns IS BOUND.
        gr_columns
        ->set_optimize( if_salv_c_bool_sap=>true ).

        CALL METHOD gr_columns->set_column_position
        EXPORTING
        columnname
        = 'CONV_DATE'
        position  = 1.

        CALL METHOD gr_columns->set_column_position
        EXPORTING
        columnname
        = 'FROM_CURR'
        position  = 2.

        CALL METHOD gr_columns->set_column_position
        EXPORTING
        columnname
        = 'FROM_AMT'
        position  = 3.

        CALL METHOD gr_columns->set_column_position
        EXPORTING
        columnname
        = 'TO_CURR'
        position  = 4.

        CALL METHOD gr_columns->set_column_position
        EXPORTING
        columnname
        = 'TO_AMT'
        position  = 5.

        CALL METHOD gr_columns->set_column_position
        EXPORTING
        columnname
        = 'EXC_RATE'
        position  = 6.

        CALL METHOD gr_columns->set_column_position
        EXPORTING
        columnname
        = 'MESSAGE'
        position  = 7.

        TRY.
        gr_column ?= gr_columns
        ->get_column( 'CONV_DATE' ).
        gr_column
        ->set_short_text( 'Conv. Date' ).
        gr_column
        ->set_medium_text( 'Conv. Date' ).
        gr_column
        ->set_long_text( 'Conversion date' ).
        CATCH cx_salv_not_found.                "#EC NO_HANDLER
        ENDTRY.

        TRY.
        gr_column ?= gr_columns
        ->get_column( 'FROM_CURR' ).
        gr_column
        ->set_short_text( 'From Curr' ).
        gr_column
        ->set_medium_text( 'From Curr' ).
        gr_column
        ->set_long_text( 'From Currency' ).
        CATCH cx_salv_not_found.                "#EC NO_HANDLER
        ENDTRY.

        TRY.
        gr_column ?= gr_columns
        ->get_column( 'FROM_AMT' ).
        gr_column
        ->set_short_text( 'From Amt' ).
        gr_column
        ->set_medium_text( 'From Amt' ).
        gr_column
        ->set_long_text( 'From Amount' ).
        CATCH cx_salv_not_found.                "#EC NO_HANDLER
        ENDTRY.

        TRY.
        gr_column ?= gr_columns
        ->get_column( 'TO_CURR' ).
        gr_column
        ->set_short_text( 'To Curr' ).
        gr_column
        ->set_medium_text( 'To Curr' ).
        gr_column
        ->set_long_text( 'To Currency' ).
        CATCH cx_salv_not_found.              "#EC NO_HANDLER
        ENDTRY.

        TRY.
        gr_column ?= gr_columns
        ->get_column( 'TO_AMT' ).
        gr_column
        ->set_short_text( 'To Amt' ).
        gr_column
        ->set_medium_text( 'To Amt' ).
        gr_column
        ->set_long_text( 'To Amount' ).
        CATCH cx_salv_not_found.              "#EC NO_HANDLER
        ENDTRY.

        TRY.
        gr_column ?= gr_columns
        ->get_column( 'EXC_RATE' ).
        gr_column
        ->set_short_text( 'Exc. Rate' ).
        gr_column
        ->set_medium_text( 'Exc. Rate' ).
        gr_column
        ->set_long_text( 'Exchange Rate' ).
        CATCH cx_salv_not_found.              "#EC NO_HANDLER
        ENDTRY.

        TRY.
        gr_column ?= gr_columns
        ->get_column( 'MESSAGE' ).
        gr_column
        ->set_short_text( 'O/P Msg' ).
        gr_column
        ->set_medium_text( 'O/P Msg' ).
        gr_column
        ->set_long_text( 'Output Message' ).
        CATCH cx_salv_not_found.              "#EC NO_HANDLER
        ENDTRY.

        ENDIF.

        CALL METHOD gr_table->display.
        ENDIF.
        ENDFORM.                    " DISPLAY

         

          7. Selection texts:

        1.jpg

         

          8. Text Symbols:

        1.jpg

         

          9. Execution process:

              Go to SE38 and execute the program “ZRHRAMTCONVERSION”.

         

        1.jpg

         

          10. Provide the Input file path as per required.

                C:\Users\raghub\Desktop\Converstion Folder\Conv_Input_File.xls

         

        1.jpg

        1.jpg

         

          11. Provide the Output file path as per required.

        1.jpg

         

          12. Click on Execute, to see the results

        1.jpg

         

          13. Output Results:

        1.jpg

         

          14. Output file downloaded like this:

        1.jpg

         

          15. Downloaded output file is as shown below:

         

        1.jpg

        Changing Fields Financial Accounting

        $
        0
        0

        A long time, I come across the following situation in many projects:

         

        • Copyof transactions: FBL1N, FBL3NandFBL5N;
        • CreateunnecessaryZ reports in FI;
        • Creating incorrect append (BSEG);

         

        Although there is great material in SDN, enough for a good development, I realize thatthere is alwaysthe same dilemma. Many times I myself was the spreader of it was not possible to include a new field in FB02, for example. SoI decided tocentralizeall thissubject in ablog, and I hopetohelpeveryone!


        To make it easierI willseparate thesubjecttopics:


        1 - Create new fields in transactions FBLXN


             There are two ways:

          • FIBF and EXIT

                       Create new product:

                    FIBF1.png      

                    FIBF2.png

                      You need copy the FM SAMPLE_INTERFACE_00001650:

                    FIBF3.png

                       Create new event 1650 for FM (Copy):

                    FIBF6.png

                    FIBF7.png

                       Eachrow processedin reports (FBL1N, FBL3N and FBL5N) will pass throughFM Z.

                       The function module has the I_POSTAB parameter that will receive the amounts already filled by the standard.

                       Simply fill out thenew field in the parameter E_POSTAB.

                       Observation: check with SY-TCODE which transaction it is being processed.

                    FIBF8.png

                       OR


          • BADI:

                       Implements the standard definition FI_ITEMS_CH_DATA.

                    BADI.png

                       In CHANGE_ITEMS method simply fill the new field (parameter CT_ITEMS):

                    BADI.png

                    BADI.png

                       The big difference is that the BTE processing is done line to line and BADI the standard "delivery" all report list.

         

             The two formsis necessary create the new fields in the structures RFPOS and RFPOSX.         

          RFPOS.png

          RFPOSX.png

             You need to runRFPOSXEXTENDviaSE38program (all environments).

             I advise you to include the execution of the program RFPOSXEXTEND in taskof yourrequest.

             ID = R3TR and TYPE = XPRA.

        SE09.png


        2 - Changedynamic selectionFBLXN

             Yes, it is possible! Don`t worry!

             First step,you must identify thelogical databaseused by thestandardin the transaction.

                 

        Transaction
        Program
        Logical DataBase
        FBL1NRFITEMAPKDF
        FBL3NRFITEMGLSDF
        FBL5NRFITEMARDDF


            





             Expand the logical database in SE36:

          SE36.png

          SE36.png

             Choose option: Copy (F7)

          SE36.png

             There is only one lock of this procedure is allowed only include some fields tables for each transaction.

             Basically the tables are displayed in the table list in transaction SE36.

             Obs.: Check SAPNote 310886.

             For example FBL1N:

        Tables.pngTables.png

           In this case, the new field it was included in BSIK. Just click on thechosentable.

           Complementingthe issue, you can also includestandardfields thatare not deliveredbydefault.

          

           It is possible create new box, for example, 04 Test.

           This way you will get a new box of courses in the dynamic selection in the transaction.

           Choose the new field andwhich foldershould be included.

        campos.png

        FBL1N.pngFBL1N.png


        3 - Change screen using coding block

             Thisis the most criticalof allitem, especially because I myselfwasthe propagatorofthis idea was not possible.

         

             First step. Create new fields in Structure CI_COBL.

             Attention: BSEG activationamong otherimportant tables.

            

             Transaction OXK3:

          OXK3.png

          CI_COBL.png

             The same step for Add Field BI, but the Structure is CI_COBL_BI.


             The step more important, add new append structure in BSEG.

             The field namesmust alwaysstartwith twoZ.

          BSEG.pngBSEG.png


             The next steps arecustomizing. 4 basic steps.

             1 - Identify which release keys should display the fields in the transaction. Setting should be made in OB41, enabling the visualization of fields.

             Example: ZZBSEG (Workflow).

          OB41.png

             2 - Identify the status-field groups that need to be modified in OBC4 (OB14), determining in ledger account level visualization of the field.

          OB14.png

             3 - Identify the types of release, account type and operating class, in the OB32 transaction to determine which types of accounts fields must be displayed.

          OB32.png

             4 - Make the setting in the table TCOBX via SM30 to determine the modification group for the field you can modify the field information.                     

          SM30.png

             RESTRICTION:   

        • In the coding block of the system you can create your own fields.
        • The new coding fields can then be used in FI General Ledger accounts, MM Inventory Management and MM Purchasing, and are also updated in the line items created in the Controlling applications.
        • The new coding fields cannot be used for account type D or K.
        • SAPNote 1607020.


        4 - Change functions in transactions FBLXN

             Under construction...


         

         

         




        SALV Editable? Yes, as per this Standard SAP Application

        $
        0
        0

        Don’t get too excited about the editable SALV solution out of the box, but I think we have a solution.

         

        Recently Paul Hardy published a blog International Editable SALV day as the 8th anniversary of the first SALV Editable question and so far no out of the box solution. To those who hasn’t read the previous so many discussion, out of box solution means a single method to make it editable. As of ABAP 740 SP05, there isn’t any new method in SALV OM which does the same thing or at least sounds like SET_EDITABLE.

         

        I developed an work around in November 2008 and got lot of criticism for that. You can see the solution and read the comments - Power of ABAP Objects: Overcome the Restrictions of SALV Model

         

        Based on the comments, customers should never use these type of workarounds. Obviously,  SAP must not use the work around as they say Editable is not intended purpose of SALV OM. But, look what I found >> Standard SAP (yes, standard SAP) has used the almost similar approach to make SALV editable which I used in my solution.

         

         

        Standard SAP Application

        I was replying to some query on CL_SALV_TREE and I did a where used on the adapter object and stumbled upon this function group FOT_RFD_MONI. Usually the adapter is used by only CL_SALV* Classes and my test program. But, suddenly I see a new program popping up in the where used list and that kept me thinking what is this program doing here?

         

        This standard SAP application is developed using this approach – almost same as the approach used in my solution. (Note, that I’m not calling it a workaround anymore ):

        • Inherit the class from the Model class
        • Create a method in the inherited class to gain access to the Adapter object – E.g. Grid, Fullscreen, Tree
        • Gain access of the underlying object – Grid or Tree

         

         

        Class definition

        Std_SAP_SALV_Editable_1_000.png

         

        Class Implementation

        Std_SAP_SALV_Editable_1_001.png

         

         

        Use of method to make Grid editable / non-editable

        Std_SAP_SALV_Editable_2_000.png

        Std_SAP_SALV_Editable_2.png

         

         

         

        Redefined Solution

        So, based on this design, I simplified my version of the approach. Created a custom class where I have couple of helper methods to gain access to the adapter and then the underlying object – either Grid or Tree.

        • GET_GRID would be used to get the CL_GUI_ALV_GRID object
        • GET_TREE would be used to get the CL_GUI_ALV_TREE object

         

        Utility Class

        *----------------------------------------------------------------------*
        *       CLASS ZCL_TEST_NP_SALV_MODEL DEFINITION
        *----------------------------------------------------------------------*
        *
        *----------------------------------------------------------------------*
        CLASS zcl_test_np_salv_model DEFINITION
          PUBLIC
          FINAL
          CREATE PUBLIC .

          PUBLIC SECTION.

            CLASS-METHODS get_grid
              IMPORTING
                !io_salv_model TYPE REF TO cl_salv_model
              RETURNING
                value(eo_grid) TYPE REF TO cl_gui_alv_grid
              RAISING
                cx_salv_error .
            CLASS-METHODS get_tree
              IMPORTING
                !io_salv_model TYPE REF TO cl_salv_model
              RETURNING
                value(ro_tree) TYPE REF TO cl_gui_alv_tree
              RAISING
                cx_salv_error .
          PROTECTED SECTION.
          PRIVATE SECTION.
        ENDCLASS.



        CLASS ZCL_TEST_NP_SALV_MODEL IMPLEMENTATION.


        * <SIGNATURE>---------------------------------------------------------------------------------------+
        * | Static Public Method ZCL_TEST_NP_SALV_MODEL=>GET_GRID
        * +-------------------------------------------------------------------------------------------------+
        * | [--->] IO_SALV_MODEL                  TYPE REF TO CL_SALV_MODEL
        * | [<-()] EO_GRID                        TYPE REF TO CL_GUI_ALV_GRID
        * | [!CX!] CX_SALV_ERROR
        * +--------------------------------------------------------------------------------------</SIGNATURE>
          METHOD get_grid.

            DATAlo_error      TYPE REF TO cx_salv_msg.

            IF io_salv_model->model NE if_salv_c_model=>table.
              RAISE EXCEPTION TYPE cx_salv_msg
                EXPORTING
                  msgid = '00'
                  msgno = '001'
                  msgty = 'E'
                  msgv1 = 'Incorrect SALV Type'.
            ENDIF.

            eo_grid = lcl_salv_model_list=>get_grid( io_salv_model ).

          ENDMETHOD.                    "get_grid


        * <SIGNATURE>---------------------------------------------------------------------------------------+
        * | Static Public Method ZCL_TEST_NP_SALV_MODEL=>GET_TREE
        * +-------------------------------------------------------------------------------------------------+
        * | [--->] IO_SALV_MODEL                  TYPE REF TO CL_SALV_MODEL
        * | [<-()] RO_TREE                        TYPE REF TO CL_GUI_ALV_TREE
        * | [!CX!] CX_SALV_ERROR
        * +--------------------------------------------------------------------------------------</SIGNATURE>
          METHOD get_tree.

            DATAlo_error      TYPE REF TO cx_salv_msg.

            IF io_salv_model->model NE if_salv_c_model=>tree.
              RAISE EXCEPTION TYPE cx_salv_msg
                EXPORTING
                  msgid = '00'
                  msgno = '001'
                  msgty = 'E'
                  msgv1 = 'Incorrect SALV Type'.
            ENDIF.

            ro_tree = lcl_salv_model_list=>get_tree( io_salv_model ).

          ENDMETHOD.                    "GET_TREE
        ENDCLASS.

         

        To get the CL_GUI_ALV_GRID:

            DATA: lo_grid   TYPE REF TO cl_gui_alv_grid.
            DATA: lo_msg    TYPE REF TO cx_salv_msg.
            TRY .
                lo_grid  = zcl_test_np_salv_model=>get_grid( o_salv ).
              CATCH cx_salv_msg INTO lo_msg.
                "appropriate handling
            ENDTRY.

         

         

        Local class LCL_SALV_MODEL_LIST which is inherited from CL_SALV_MODEL_BASE. Previously, I used the CL_SALV_MODEL_LIST to inherit my helper class, which wouldn't provide me the instance for the TREE. Thus, moved higher in the class hierarchy.

        *"* use this source file for the definition and implementation of
        *"* local helper classes, interface definitions and type
        *"* declarations

        *
        CLASS lcl_salv_model_list DEFINITION INHERITING FROM cl_salv_model_base.

          PUBLIC SECTION.

            CLASS-METHODS:
              get_grid
                IMPORTING
                  io_salv_model TYPE REF TO cl_salv_model
                RETURNING
                  value(ro_gui_alv_grid) TYPE REF TO cl_gui_alv_grid
                RAISING
                  cx_salv_msg,

              get_tree
                IMPORTING
                  io_salv_model TYPE REF TO cl_salv_model
                RETURNING
                  value(ro_tree) TYPE REF TO cl_gui_alv_tree
                RAISING
                  cx_salv_msg.


        ENDCLASS.                    "lcl_salv_model_list DEFINITION
        *
        CLASS lcl_salv_model_list IMPLEMENTATION.
          METHOD get_grid.
            DATA:
             lo_grid_adap TYPE REF TO cl_salv_grid_adapter,
             lo_fs_adap   TYPE REF TO cl_salv_fullscreen_adapter,
             lo_root      TYPE REF TO cx_root.

            TRY .
                lo_grid_adap ?= io_salv_model->r_controller->r_adapter.
              CATCH cx_root INTO lo_root.
                "could be fullscreen adaptper
                TRY .
                    lo_fs_adap ?= io_salv_model->r_controller->r_adapter.
                  CATCH cx_root INTO lo_root.
                    RAISE EXCEPTION TYPE cx_salv_msg
                      EXPORTING
                        previous = lo_root
                        msgid    = '00'
                        msgno    = '001'
                        msgty    = 'E'
                        msgv1    = 'Check PREVIOUS exception'.
                ENDTRY.
            ENDTRY.

            IF lo_grid_adap IS NOT INITIAL.
              ro_gui_alv_grid = lo_grid_adap->get_grid( ).
            ELSEIF lo_fs_adap IS NOT INITIAL.
              ro_gui_alv_grid = lo_fs_adap->get_grid( ).
            ELSE.
              RAISE EXCEPTION TYPE cx_salv_msg
                EXPORTING
                  msgid = '00'
                  msgno = '001'
                  msgty = 'W'
                  msgv1 = 'Adapter is not bound yet'.
            ENDIF.


          ENDMETHOD.                    "get_grid

          METHOD get_tree.

            DATA:
             lo_tree_adap TYPE REF TO cl_salv_tree_adapter,
             lo_root      TYPE REF TO cx_root.

            TRY .
                lo_tree_adap ?= io_salv_model->r_controller->r_adapter.
              CATCH cx_root INTO lo_root.
                RAISE EXCEPTION TYPE cx_salv_msg
                  EXPORTING
                    previous = lo_root
                    msgid    = '00'
                    msgno    = '001'
                    msgty    = 'E'
                    msgv1    = 'Check PREVIOUS exception'.
            ENDTRY.

            IF lo_tree_adap IS NOT BOUND.
              RAISE EXCEPTION TYPE cx_salv_msg
                EXPORTING
                  msgid = '00'
                  msgno = '001'
                  msgty = 'W'
                  msgv1 = 'Adapter is not bound yet'.
            ENDIF.
            ro_tree = lo_tree_adap->r_tree.

          ENDMETHOD.                    "get_tree
        ENDCLASS.                    "lcl_salv_model_list IMPLEMENTATION

         

        To get the CL_GUI_ALV_TREE:

          DATA: lo_tree TYPE REF TO cl_gui_alv_tree.
          TRY .
              lo_tree = zcl_test_np_salv_model=>get_tree( gr_tree ).
            CATCH cx_salv_msg.

          ENDTRY.

         

         

        Updated version of the ON_USER_COMMAND method in the original solution Power of ABAP Objects: Overcome the Restrictions of SALV Model

          METHOD on_user_command.
            DATA: lo_grid TYPE REF TO cl_gui_alv_grid.
            DATA: ls_layout TYPE lvc_s_layo.
            CASE e_salv_function.
        *     Make ALV as Editable ALV
              WHEN 'MYFUNCTION'.
        *>>       
                TRY .
                    lo_grid = zcl_test_np_salv_model=>get_grid( lo_report->o_salv ).
                  CATCH cx_salv_msg.
                ENDTRY.
        *<<       
                IF lo_grid IS BOUND.
        *         Editable ALV
                  ls_layout-edit = 'X'.
        *         Set the front layout of ALV
                  CALL METHOD lo_grid->set_frontend_layout
                    EXPORTING
                      is_layout = ls_layout.
        *         refresh the table
                  CALL METHOD lo_grid->refresh_table_display.
                ENDIF.
            ENDCASE.
          ENDMETHOD.                    "on_user_command

         

         

         

        Questions are ...

        These also raises to these questions:

        • Is SAP not planning to have SET_EDITABLE method in OM as they have started using this approach?
        • Would SAP now provide support if customer uses the same approach and run into issues?
        • If no, than how come SAP approved this design to exist it in customer’s system?
        • If yes, As you can see, the application was created in somewhere 2011 by SAP. So, the developer must be aware of the existence of my solution. Shouldn't I get at least a some credit? Not that I want, but would be good to have,

         

        Please share your thoughts...

         

         

        updated on 23-Jun-15 at 8:30 AM CST to add the missing local class

        A Lisp interpreter in ABAP

        $
        0
        0

        saplambda.jpgI have long thought about writing a Lisp interpreter in ABAP, after coming across an article by Peter Norvig entitled "(How to Write a (Lisp) Interpreter (in Python))". In that article, he shows how to construct a Lisp interpreter in what amounts to only a few lines of Python. Anthony Hay is someone, also inspired by Norvig's article, who did the same thing in C++ with his article entitled "Lisp interpreter in 90 lines of C++". (In the article he does point out that the 90 lines are for the core of the interpreter, not the built-in functions).

         

        Well, taking the lead of their work, especially getting some insight from Hay, I have built a Lisp interpreter in ABAP now, though obviously it was never going to be in just 90 lines. In fact, doing most things in 90 lines of ABAP or less is somewhat of a Zumutung.

         

        Before looking at the result though, let me give you a little background on Lisp.

         

        Lisp: An executive summary

         

        Lisp, which is an acronym for "LISt Processing", is one of the oldest high-level computer languages, dating back to 1958, with only Fortran being older. You could say that Lisp is the oldest surviving computer language, as (I think) no-one uses Fortran anymore, except perhaps hobbyists. Correction: I have just read up a bit on Fortran and it seems there are still people using it for scientific applications. My apologies, Fortran lovers <3

         

        In spite of being a product of the late 50s, Lisp implemented some way-ahead-of-its-time concepts, some which have started to appear in mainstream languages like Java only recently (the introduction of closures, for example), and some which have yet to make their appearance*(1).

         

        Lisp, which is often regarded as a functional language (though not limited to that) is incredibly powerful and, due to its association with Artificial Intelligence, has been used widely by academia. However, it has found a very loyal following among a large contingency of IT specialists. Today it exists primarily in two dialects: Common Lisp and Scheme.

         

        With the resurgence of interest in functional programming (consider also the recent SCN article on Functional Programming in ABAP), Lisp is getting a lot more attention these days, and this is a good thing.

         

        Here is an example of List code:

         

        (defundouble (x) (* x 2))

         

        This defines a function called "double", which takes one argument, x, which it multiplies by two. (I have to warn you upfront though, that this is Common Lisp, and the Scheme syntax for defining a function, which we will use, is slightly different - read on).

         

        Lisp code consists of parenthesized lists. A function call is constructed of a list in which the first element is the name of the function, and the elements following that are its arguments. In the example above, "defun" is the name of a function that creates a function, the first parameter is the name of the function, the second is a list of the arguments the function will receive, and the last is the body that will be evaluated when the function is called.

         

        To call our function, we would write the following:

         

        (double 7)

         

        which, if it were evaluated, would give us 14. It may seem weird at first, especially when putting mathematical operators at the beginning of an expression, like (* 3 7), but it does become easier as you carry on.

         

        An interesting aspect of Lisp is that code and data are represented in the same way. (Lists are just data after, all). This makes Lisp what is called a "homoiconic" language, and has some interesting implications, namely that if your code can emit data, it can potentially also emit other code, and this is in fact something that people do with Lisp: they write programs that construct other programs*(2).

         

        There are numerous reasons to learn Lisp, and Eric S. Raymond, a well-known open source pundit, makes the following alluring statement:

         

        LISP is worth learning for a different reason — the profound enlightenment experience you will have when you finally get it. That experience will make you a better programmer for the rest of your days, even if you never actually use LISP itself a lot.


        A Lisp interpreter in ABAP


        Now I will show you the Lisp interpreter I have built in ABAP. It's not exactly a piece of art, more the result of some late-night slogging away at the keyboard, lots of testing and troubleshooting and prayer. It's not coding poetry, but it's functional (if you can excuse the terrible pun), and I think it's pretty cool.

         

        Although Norvig's (and therefore Hay's) interpreters clearly lean toward the Scheme persuasion, I at some point diverged and started using the book "Common Lisp: A Gentle Introduction to Symbolic Computation" as a reference for my implementation, primarily because I changed at one point from representing lists as internal tables of cells to using linked lists (cons cells). This is because, even though there are some performance gains to using tables, e.g. determining the size of a list, I ended up copying parts of tables everywhere, and this leads to greater memory consumption. The result is that I have adopted the Common Lisp approach to some things (e.g. false is represented by nil).

         

        Apart from the pure fun in doing this, the great thing about implementing your own Lisp is that you are not bound to any particular standard. For example, for historical reasons, there are some weird names for functions dealing with lists, such as CAR (which retrieves the first element of a list) and CDR, which returns the remainder of the list, both important constructs. If I wanted, I could rename them HEAD and TAIL (and maybe I will).*(3)

         

        The great thing about creating a Lisp interpreter for ABAP is that now you have an interactive means to write code on your ABAP application server, which opens up all sorts of possibilities. Imagine coding business rules in Lisp, for example.

         

        Trying it out

         

        To try out the interpreter, you will have to download the source from the following GitHub repo and install it on your system: mydoghasworms/abap-lisp · GitHub. By my own convention, the source files starting with 'ZUSR' must go into a type '1' (report) program, and the 'ZLIB' programs into a type 'I' (Include) program*(4). At minimum you will need ZLIB_LISP, which contains the interpreter, and ZUSR_LISP_REPL, which is just a simple REPL (Read-Eval-Print Loop) front-end to the interpreter which consists of s single input and output field. (Sorry, no fancy multi-line inputs and script parsing at this point; hopefully in the future).

         

        When you run ZLIB_LISP_REPL, you will get the following:

         

        http://i.imgur.com/BelJ9DH.png

        Now type in a number in the INPUT field and press Enter. The REPL responds with the number, because that is an expression on its own and a number will just evaluate to itself.

         

        http://i.imgur.com/ktyCPTR.png

         

        That's not very impressive. Now enter something different, like 'a':

         

        http://i.imgur.com/LqGBtt7.png

        The interpreter responds correctly by telling us that the symbol 'a' is not defined. Let's define it with some value:

         

        (definea 22)

         

        The REPL will respond with the defined symbol, which is 'a'. If you enter 'a' now, it will respond with 22.

         

        Now let's define a function called 'abs' to calculate the absolute value of a number. (I told you earlier that the syntax is somewhat different in Scheme. I wasn't lying):

         

        (defineabs (lambda (n) ((if (> n 0) + -) n)))

         

        The built-in function 'lambda' creates a function which we can call, and by using that as input to the value portion of the built-in 'define' function, we assign a name to it that we can reference later. The body of the function makes use of a few built-in functions, namely 'if', '>' (greater than), '+' (add) and '-' (minus).

         

        Let us try and execute the new function:

         

        (abs -22)

         

        If everything went well, the interpreter should respond with 22.

         

        We could have written the 'abs' function as follows and produced the same result:

         

        (defineabs (lambda (n) (if (< n 0) (- n) n)))

         

        The first way, however, demonstrates an interesting feature: In the first definition of the 'abs' function, the invocation of 'if' evaluates to a function, which is either '+' or '-', that is then subsequently applied to the input, n. This technique of determining the function to call based on some criteria within an expression is a powerful construct, and not something you would necessarily be used to from imperative or object-oriented programming. Needless to say, a transition to Lisp (and functional programming for that matter) involves a big mind-shift.

         

        Let's carry on: here is an example of a recursive function to calculate a factorial:

         

        (definefact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))

         

        Run it with a few numbers and see what happens. If you run it with 2000, for example, you will probably notice a significant delay. With an input of 3000, you will cause a CX_SY_ARITHMETIC_OVERFLOW exception. (At least I assume that the ABAP kernel manages the same data types consistently across platforms).*(5)

         

        http://i.imgur.com/jNQmgxJ.png

        Lastly, let's play around with lists a little. Create a list and assign it to a symbol with the following:

        (definemylist (list1234) )

        You can check the result by evaluating the symbol 'mylist'. Now, to get the first element of the list:

        (car list)

        and to get the remainder (i.e. the rest of the list starting after the first element:

        (cdr list)

         

        To try out some more examples and see some code showing the interpreter in action, look at the ZUSR_LISP_TESTS program.

         

        A short reference

         

        The following is a short reference of the built-in functions available in the interpreter. Some are available as symbols which map to native procedures which, like the functions you define, can be passed around in expressions. The other list are just symbols that are directly evaluated by the interpreter and by contrast, can not be passed around in expressions*(6).

         

        These are the functions you can use in the current interpreter:

         

        FunctionArgumentsFirst-class*Description
        +, -, *, /1-nYesBasic arithmetic functions
        append2YesTakes an element and a list, appends the element to the list
        list1-nYesMakes a list out of all given arguments
        length1YesLength of the given list
        car1YesReturns the first item in a list, or nil given an empty list (or nil)
        cdr1YesReturns the tail of a list, which will be a list, or nil, given an empty list (or nil)
        cons2YesConstructs a new list, making the first element the head and the second input (usually a list) the tail (like prepending, almost)
        nil?, null?1YesTests if the input is nil or not (null? is an alias added for compatibility with Scheme-based tests)
        >, >=, <, <=1-nYesComparison operators
        equal?2YesTests if the given arguments are equal (either by symbol or number) or, in the case of a list, whether it is the same list.
        quote1NoReturns the argument verbatim, without evaluating it
        if2-3NoEvaluates the first argument. If true, the second argument is evaluated. If not, a third argument is evaluated, otherwise nil (false) is returned
        define2NoAssigns a value (second argument) to a symbol (first argument)
        lambda2NoDefines an anonymous function; first input is a list of formal parameters; second is the body to be evaluated
        begin0-nNoEvaluates all arguments as expressions in turn; a nice way to string a lot of expressions together to be executed

        *meaning you can use it as an argument or return-value. I'm not sure what the correct term is


        There are also two other built-in values: nil and true. Anything that is not nil can be considered true. nil is essentially the same as an empty list, and this is more of a Common Lisp thing. Scheme also has a true keyword.


        Where to from here? (Caution: Rough edges!)

         

        While there is a certain thrill in creating a Fibonacci function and watching it execute, knowing that the code is being parsed, built into an abstract syntax tree and evaluated in ABAP, you quickly grow tired of it. What would make an interpreter like this really useful, is if you can interact with the rest of the system, by accessing the database or particularly consuming classes and function modules. It should be fairly straightforward to extend the list of built-in types in the interpreter and provide a set of built-in functions to achieve this.

         

        At the moment, the interpreter lacks the notion of a string type, as well as other syntactic shortcuts (like prepending a symbol or list with a single quote, which is the shortcut for quoting something without the need to use QUOTE). That is definitely on the list. (Sorry, another bad pun).

         

        There are other aspects to consider, such as the performance. Many applications in functional programming involve making use of recursive functions, and for this reason, many implement what is called "Tail Call Optimization", which eliminates the need for a growing stack*(7). Inherently, this implementation makes use of the stack in ABAP, so perhaps another approach would be to realize the evaluation of functions using other ABAP constructs.

         

        Also, I have not done a lot of negative testing, especially on the parsing side, so don't be surprised if you come across problems. For now I assume all input is valid. (Feel free to raise an issue on the GitHub repo though. It would warm my heart to know that someone is actually playing with this).

         

        Further Reading

         

        Lisp makes an interesting topic of study, and there are many wonderful resources on the internet. I haven't delved so much into all the technical aspects, so I am not going to try and recommend any books I have not read myself. Instead, because I love sensationalism, I have spent a lot of time reading blogs by people who rave about Lisp. One of the best reads I can recommend is an article by Paul Graham entitled "Revenge of the Nerds".

         

        While on the subject of futuristic, ahead-of-their-time languages, I could not finish this article without pointing you to REBOL, a language developed by Carl Sassenrath, one of the brains behind Amiga in the 90s. It's not Lisp, but it embodies some of its spirit. You can almost think of it as "Lisp without the parentheses" (though I'm sure that hardly does it justice; it is probably the best thought-out language on the planet). It does a fantastic job of looking like an ordinary procedural language, but with the power of Lisp. REBOL is not developed so much anymore, but it's spiritual successor, the Red Language is what you need to keep your eye on. If robots ever take over the world, the AI would be written in Red.

         

        *Footnotes

        (1) See the link to the Paul Graham article somewhere in this post, where these features are listed.

        (2) You have probably done this too before in ABAP if you have used the GENERATE SUBROUTINE POOL statement to generate code on the fly, which is a technique that was used more frequently in the past before the availability of more dynamic statements in ABAP. I can assure you though, that it is very rudimentary compared to what Lisp has to offer.

        (3) The explanation is that on the hardware on which Lisp was initially implemented, CAR stood for "Contents of the Address portion of the Register" and CAR stood for "Contents of the Decrement portion of the Register", i.e. references to assembly instructions.

        (4) In hindsight, ZBIN would be more applicable to executable programs; it's more Unix-like

        (5) It should be noted that the interpreter makes use of the types provided by ABAP, so there is no support for big numbers like in other languages. But in 40 years of SAP, I don't think anyone has complained of not being able to count a gazillion dollars.

        (6) I just followed the lead of Norvig and Hay on this. In some cases it is obvious that if the function is independent of context, it is easy to use as a procedure. This may warrant a little closer inspection though.

        (7)though admittedly ABAP seems to have no problem with sky-high stacks, probably because an application server is normally configured with a lot of memory.

        Why it’s not a good practice to use text-symbols as literals in your code

        $
        0
        0


        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 usingl_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.

        SALV Editable with Single (custom) Method

        $
        0
        0

        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

         


        Chrome extension to highlight ABAP correction instructions in SAP Notes

        $
        0
        0

        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.

         

        Best Regards,

        Guilherme.

        Screen hacks

        $
        0
        0

        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.

        Static Class Vs Singleton Class – What to use when

        $
        0
        0

        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.




        An Insight into Exception Handling

        $
        0
        0

        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
        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



        (You Gotta) Fight, for Your Right (To...

        $
        0
        0

        ... 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

        Many help is already in your SAP system

        $
        0
        0

        Hi,

         

        Before go anywhere else search your solution in your system, might possible, you can get the solution there itself.

         

                                                                      info.PNG

         

        Facing every day lots of issues and trying to providing the suitable answers for the raised issue.

         

        But one think always comes in my mind, are the people are just posting their blog without any analysis, May be I am wrong, but when I go through with question, I found, the answers is already available in SAP system.

         

        Only the thing is members are not analyzing in system first. In SAP system already numbers of program available to get the basic idea about any one of the component.

         

        Here is some bunch of programs, which has provided by SAP to get you hand dirty before posting an question in Community on below objects.

         

        Note: the all Program belongs to Net weaver Gateway 740 system.

         

        1. HTML container :

                  Program Name:  BCALV_DEMO_HTML

                  Displaying the information in HTML format

         

                  info.PNG

         

        2. SAP OOPS Concepts:

             Program Name : DEMO_ABAP_OBJECTS

             This is very nice program, which has explained the OOPS concept in ABAP way.

        info.PNG

        After execute, the above pop-up window will open to see the information, but yes, how is related to selected concept, that you have to check in program only.

         

        3. MIME Objects with Tree ALV  :

             Program Name :       DEMO_CFW

                                              DEMO_CFW_1,

                                              DEMO_CFW_2,

                                              DEMO_CFW_3

         

             This is a demo program to call MIME object from SAP to display in ALV grid header.Also contained the node event functionality with ALV refresh (which is very common question in community with respect to ALV refresh).

             info.PNG

         

        4. Dropdown List with F4 :

             Program Name : DEMO_DROPDOWN_LIST_BOX

             Even I also surprise sometime to see this kind of program, yes, sometime choose a bit complex way to get the same solution what we can get through      other simple way.

             In this demo program, SAP used F4IF_INT_TABLE_VALUE_REQUEST function module and display the all F4 help information in drop down list.

             info.PNG

         

        5. STEP LOOP :F4_help

             Program Name : DEMO_DYNPRO_STEP_LOOP

             It’s kind of table, but all cells are display as individual input fields. A very basic an easy program to understand how step loop works.

             info.PNG

        6. Timer Class:

             Program Name : SAP_TIMER_DEMO

             This is a very simple but very well explained program about the time class.

             Please go through with code page to understand the logic.

             Output is based on entered interval, the output will return the page refresh counter information.

         

        7. Pictue Class Demo :

             Program Name :     SAP_PICTURE_DEMO_ICON

                                             SAP_PICTURE_DEMO_DRAG_DROP

                                             SAP_PICTURE_DEMO

         

        En example program to display picture and different method with respect to change picture resolution and position and another program has cover the drag-n-drop function of picture from one container to another one.

             info.PNG

        8. Display PDF file in PDF container:

             Program Name : SAP_PDF_VIEWER_DEMO

         

             This program to Display a PDF file in SAP window.

             Though SAP has did some hard coding for PDF path, so while executing, it will give dump, but

             It’s a very and understandable program to get the logic.

         

        9. Text Editor :

             Program Name :      SAPTEXTEDIT_DEMO_2_OLD

                                             SAPTEXTEDIT_DEMO_1

                                             SAPTEXTEDIT_DEMO_3

         

        Last few days, I face some question regarding text editor class, here is some demo program, which cover most of the functionality, like load data from file.

        Position of the cursor, current line etc.

             info.PNG

         

        9. Display Word and Excel file inside SAP window

             Program Name:      SAPRDEMO_TABLES_IN_EXCEL

                                            SAPRDEMO_WORD_NOTEPAD

                                            SAPRDEMO_MAILMERGE_INTERFACE,

                                            SAPRDEMO_FORM_INTERFACE,

                                            SAPRDEMO_PIVOTINTERFACE

                                            SAPRDEMO_SPREADSHEET_INTERFACE

             The above programs are well explain i_oi_spreadsheet& i_oi_document_proxy class, which are use for display Word and Excel file inside a container.

             info.PNG 

             Also, it’s explained about PIVOT functionality and how it can integrate with SAP screen.

             info.PNG

         

        10 HTML UI Demo:

             Program Name : SAPHTML_UI_DEMO

          

             This is an example to open browser in SAP window and also active or inactive some of the functionality of browser.

             info.PNG

        11. SAP HTML Events:

             Program Name:     SAPHTML_DEMO_CN

                                           SAPHTML_EVENTS_DEMO

                                           SAPHTML_LONGTEXT_DEMO

                                           SAPHTML_R3HTTP_DEMO

                                           SAPHTML_SCRIPT_DEMO .

             The all above demo programs are demonstrate the HTML display in SAP screen with HTML event handle through SAP and get the result back to SAP screen.

         

             info.PNG

        12. Chart Program:

             Program Name:          RCC_TEST_PIE_DEMO

                                                RCC_DEMO_BASIC_ENGINE

                                                RCC_TEST_DEMO1

                                                RCC_TEST_DEMO2

                                                RCC_TEST_HISTOGRAM_DEMO.

             The above programs are based on RCC classes.

           info.PNG


        13. IGS Functionality:

             Program Name :         GRAPHICS_IGS_ZIPPER_DEMO

                                                GRAPHICS_GUI_CE_DEMO

                                                GRAPHICS_GIS_VIEW_DEMO

         

        Internet Graphical Server (IGS), it’s another functionality which works through internet graphical service.

        Though this service also chart can be draw on SAP screen, though, it will a html based chart, so HTML event can also be attached to it.

        Also a Zipper program is there to create a ZIP file through SAP.

             info.PNG


        14. ALV Gui Grid:

             Program Name:      BCALV_DND_01

                                            BCALV_EDIT_01                  Switch on and off the ready-for-input status of the entire grid

                                            BCALV_EDIT_02                  Define ready-for-input status at cell level

                                            BCALV_EDIT_03                  Verification of modified cells

                                            BCALV_EDIT_04                  Delete and append rows

                                            BCALV_EDIT_06                  Dropdown Listbox at Column Level

                                            BCALV_EDIT_08                  Integrate Non-Standard F4 Help

                                            BCALV_EDIT_09                  Define ready-for-input status at cell level

                                            BCALV_GRID_01                  Processing Print Events

                                            BCALV_GRID_03                  Detail List in Dialog Window

                                            BCALV_GRID_04                  Display Exceptions (LEDs or Traffic Lights)

                                            BCALV_GRID_07                  Define a Menu in the Toolbar

                                            BCALV_GRID_11                  Test for new layout function modules

                                            BCALV_TREE_01                  ALV Tree Control: Build Up the Hierarchy Tree

                                            BCALV_TREE_02                  ALV Tree Control: Event Handling

                                            BCALV_TREE_03                  ALV Tree Control: Use an Own Context Menu

                                            BCALV_TREE_04                  ALV Tree Control: Add a Button to the Toolbar

                                            BCALV_TREE_06                  ALV tree control: Icon column and icon for nodes/items

                                            BCALV_TREE_DEMO                Demo for ALV tree control

                                            BCALV_TREE_DND                 ALV tree control: Drag & Drop within a hierarchy tree

                                            BCALV_TREE_DND_MULTIPLE        ALV tree control: Drag & Drop within a hierarchy tree

         

        Now my favorite part comes, most of the question from ALV grid only. All above program contains different functionality of ALV grid, which is class based program

             info.PNG

        15.     CL_GUI_ALV_GRID : Style in ALV with Cell Information as tool tip.

                  program Name :     BCALV_DEMO_TOOLTIP

                  This program contains lots of answer, which asked by lots of members.

        1. Color in Field Cells
        2. Tool tip on Fields value
        3. Icon on Cells
        4. Multiple Colors in different Cells

             info.PNG

         

        15. SAP Business Graphics Tool :

              Program Name :                    BCGRDEMO

              This is a bit different but very interesting demo to display the information in graphical mode in SAP R/3 Graphic window.

                   After select any one of the hierarchy, you will a pop-window to get the receptive information. Then above chart information will come though selection of the same pop-up.

                info.PNG

        16. Messages:

             Program Name :     CNV_MBT_DEMO_ULV_01

                                            CNV_MBT_DEMO_ULV_02

                                            CNV_MBT_DEMO_ULV_03

                                            CNV_MBT_DEMO_ULV_04

                                            CNV_MBT_DEMO_ULV_05

        This all above programs has demonstrate, how we can display the message with different views.

        And how to show and hide other options from message window.

         

        info.PNG

            


        Like the above sample program, other programs also available in system,

        Keeping my blog in editable mode to add some more interesting sample program.

         

        As per our requirement, we can search 1st in out system before posting a question in communities.


        Happy learning

        Praveer. 

        An Insight into Exception Handling - 2

        $
        0
        0

        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.


        SAP Inside Track Munich

        $
        0
        0

        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

        Displaying header details in delivery while creating outbound delivery using BAPI_OUTB_DELIVERY_CREATE_STO

        $
        0
        0

        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.

        Let he is who is without ABAP code sin CAST the first stone

        $
        0
        0

        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

         

        Utility Program to download an IDOC from one system to another in the same landscape

        $
        0
        0

        Hello All,

                I have seen this as a common requirement while working for clients that an IDOC from say a Production system needs to be copied to Development/Quality system for the purpose of testing or may be from Quality system to Development system. The reasons could be :

         

        1. Production is not meant for testing the issues.

        2. The IDOC is quite bulky that the generation in other systems requires lots of effort.

        3. Testing something quick without the need of developer.

         

        After searching in SDN, i do find certain techniques but those are more than one step processes, meaning you need to do one thing and then you need to do the second thing etc to finally copy the IDOC.

         

        I now found a way to copy the IDOC from one system to another within the same landscape. i am attaching the code for doing so.

        Text Types used in Smartforms

        $
        0
        0

        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

        Viewing all 948 articles
        Browse latest View live


        <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>