Quantcast
Channel: SCN : Blog List - ABAP Development
Viewing all 943 articles
Browse latest View live

Working with Zebra Printers..

$
0
0

This blog introduces links to my documents which deals with the process of working with Zebra printers

 

These documents are based on my project learnings. I was meant to design barcode labels (both generic and 2D barcodes labels) to print on Zebra printers. I faced a little difficulty finding information related to the way of working with zebra printers using smartforms and eventually had to browse through a lot of information on web.  After a few trials based on the information i found, i was able to successfully deliver the requirement. Then i decided to document my experience with Zebra printers.

 

The first of the documents is Way of working with Zebra printers using smartforms which lists the challenges i faced and details the solution for the same in the later part of the document. it also explains the process of printing the generic barcodes and solving their alignment issues.

 

The second document  How to Print Barcodes that are not supported by SAP using Smartforms in particular delas with printing Data Matrix barcode (DMC) on Zebra printers, but in general it is also a reference document to print any 2D barcodes that are not supported by SAP. I have not tried it on other printers but the process should be same when working with any other printer.

 

Hope this makes a difference..


Too many cooks spoil the broth

$
0
0

Dear Solution Managers(Friends),


"This is the limit of tolerance". I am writing this blog to collect people's point of view especially ABAPer how they tackle functional person's logic when they are more than one.

While working on xyz project / functionality / report many times I am encounter such kind of situation where I am getting too many logic for the single task from so many functional person. Each of them tries proves that they are correct. I don't know on what basis they are providing their functional logics and how to proofing them it as correct one. I know many of us have gone through suck kind of situations I would like to know what to do when such sort of time has arrived.

User wise case to case system is neither preferable nor reliable (Strongly not possible). Sometimes I am scared to think what to do if one more selection do exists (Execute report for which user)



 

 

It's totally horrible to dream also. Sometime I feel like SAP has provided so many standard procedures (functional logic) why not to follow one them which is suitable and compatible. Then go behind and thinking ok it's for Z and all is better if user needs something another.




I am eagerly wanted to know the experience faced by others. I am getting same kind of experience or I am the only the case and if second option is activated then you may think that I am Alien on earth.



 

-Avirat

BTE – Business transaction events “demystified”.

$
0
0

Business transaction events (BTE) are one of the least popular enhancement techniques according to me. One of the reasons could be lack of awareness of exact functionality of how BTE’s work.

 

My attempt here through this blog is to spread awareness among SAP practitioners who are not aware or partially aware of BTE functionality.

 

What makes a BTE?

 

BTE is one of the many enhancements techniques available in ABAP developer’s arsenal to meet various business needs.


A BTE has a predefined interface which helps to add additional functionality through function module defined in BTE customizing. The BTE is triggered by the SAP with a call to function OPEN_FI_PERFORM_ or OUTBOUND_CALL_ which checks for any active BTE’s in customizing.


Popular misconception that many have is that the BTE functionality/scope is limited to FICO but its not …BTE can also be effectively used for requirements in OTC, PTP, APO, CRM and IS.


There are two types of interface available for implementing BTE’s:

a) Publish and Subscribe Interface

b) Process Interface

Publish & Subscribe interfaces (P&S)

These generally refer to events as documents being entered into SAP which needs to be sent to legacy systems:

• Master record was created, changed, or blocked

• Document was entered, parked, changed, or reversed

• Items were cleared or reset

What additional functionality can we achieve for above events is below:

• Starting a workflow

• Sending changes to legacy systems.

 

Process Interface

These interfaces are used to control a business process differently than the way in which it is handled in the standard R/3 System. They intervene in the standard process, and return data to the SAP application.

 

Using this functionality we can for example mail remittance advices to Vendors.

 

BTE options.JPG

But what are these options we observe above? What exactly do they mean and which one would I need to choose?

 

Here is the answer


The option ‘of an SAP application’ is mostly used by SAP for country specific enhancements.


The option 'of a Partner' is intended for the use of companies which write additional products to enhance the standard SAP and sell this software. They should have a partner-namespace (something /***/) registered by SAP. So all the functions in these enhancements should begin with that /***/.


The option ‘of a Customer’ is intended for Customer-specific development - all functions used there should be named according to the Y/Z-namespace.


Most of developer community would use ‘of a Customer’ option.


Sound’s interesting??


But how do I find a BTE?


OK lets get to business now


  • Use transaction FIBF menu Environment->Infosystem(Processes) to get a list of BTE's available. Use the Documentation button to see the documentation for each BTE.


Options.JPG


BTE.JPG

  (or)

  • Search the source code for "OPEN_FI_PERFORM" or " OUTBOUND_CALL_"


Ok I got how to search the BTE now, but how to implement it?


Patience brother here it is:


Steps to be followed:

  1. Search the BTE relevant for your requirement using FIBF transaction.
  2. Copy the sample function module for that BTE into your own ‘Z’ function module and put a break point.
  3. Goto Products-> Processes(customer) create a new entry and enter the product name description and RFC destination if applicable. Then activate the product by clicking on checkbox ‘A’.
  4. The next step is to link the function module and the event using the product created. Goto Process Modules -> of a customer and create entry enter the event and the function module for Product created in step 3.
  5. Now its testing time Go to the transaction for which you have created BTE and execute and debugger would stop at break point in ‘Z’ function module which you have created.

 

The transaction codes to be explored for BTE are below:


BERE Business Event Repository

BERP Business Processes

BF31 Application modules per Event

BF32 Partner Modules per Event

BF34 Customer Modules per Event

BF41 Application Modules per Process

BF42 Partner Modules per Process

BF44 Customer Modules per Process


Hope you had enjoyed reading this blog

Custom subscreen on standard Transaction EEDM09, EEDM10 and EEDM11

$
0
0

Requirement:

Requirement is to add few fields on the SAP standard transaction EEDM09, EEDM10 and EEDM11. In Standard screen, it should appear under Header tab with one more tab “other”. These custom screen values should be updated in the standard table EUIHEAD.

If the data already exists during EEDM10 and EEDM11 transaction, the values should display on the screen.

Technical Solution: Identified the BADI ‘ISU_EDM_POD_CUSTOMER’ to attach sub screen on standard SAP Transactions EEDM09, EEDM10 and EEDM11.

 

Step 1:

TABLE: Include structure CI_EUIHEAD with necessary fields (ZZNAME1 and ZZMOB) in the standard table EUIHEAD.

 

tb1.png

Step 2:

Create a Module Pool Program ‘SAPMZTEST’ with screen number ‘800‘.

Make the screen as sub screen by clicking sub screen radio button on ‘Attributes’

Add two fields, ZZNAME1 and ZZMOB referred from data dictionary ‘EUIHEAD’ in layout as shown below.

lay1.png

In Flow Logic,write logic as shown below

 

tables : euihead.

 

DATA: obj TYPE REF TO IF_EX_ISU_EDM_POD_CUSTOMER.

 

DATA: im_head TYPE ISU_EUIHEAD_CUSTOMER,

im_head1 type euihead,

im_con TYPE ISU00_OBJ_CONTR.

 

Process Before Output write the logic as shown below

 

 

MODULE STATUS_0800 OUTPUT.

IF obj IS INITIAL.

CALL METHOD CL_EXITHANDLER=>GET_INSTANCE_FOR_SUBSCREENS
CHANGING
INSTANCE                      = obj
EXCEPTIONS
NO_REFERENCE                  = 1
NO_INTERFACE_REFERENCE        = 2
NO_EXIT_INTERFACE             = 3
DATA_INCONS_IN_EXIT_MANAGEM   = 4
CLASS_NOT_IMPLEMENT_INTERFACE = 5
others                        = 6
.
if sy-SUBRC = 0.
CALL METHOD obj->GET_DATA_FROM_SCREEN
IMPORTING
EXP_CONTROL_DATA      = im_con
EXP_EUIHEAD_CUST_DATA = im_head
.

if im_head is not INITIAL.
euihead-zzname1 = im_head-zzname1.
euihead-zzmob = im_head-zzmob.
endif.
endif.

Make these custom fields as non-editable during display Mode(EEDM11)
IF im_con-wmode = '1'.         
LOOP AT SCREEN.
SCREEN-INPUT = 0.
MODIFY SCREEN.
ENDLOOP.
ENDIF.
endif.

ENDMODULE.                 " STAT

 

In Process After Input, write logic as shown below

MODULE USER_COMMAND_0800 INPUT.

if obj is not INITIAL.

clear: im_head1.

im_head-zzname1 = euihead-zzname1.
im_head-zzmob = euihead-zzmob.

MOVE-CORRESPONDING im_head to im_head1.

CALL METHOD obj->PUT_DATA_TO_SCREEN
EXPORTING
IMP_CONTROL_DATA = im_con
IMP_EUIHEAD_DATA = im_head1
.
endif.

ENDMODULE.

Now save and activate the Module Pool Program

Step 3

Go to transaction SE19 and create implementation Class ‘ZCL_IM_ISU_EDM_POD’

For the standard BADI ‘ISU_EDM_POD_CUSTOMER’.

Go to sub screen tab on the implementation Class and assign our Module pool program and screen number as follows.

imp1.png

We have two methods to be implemented based on our requirement in the interfaces tab namely,

PUT_DATA_TO_SCREEN.

GET_DATA_FROM_SCREEN.

In PUT_DATA_TO_SCREEN, write logic as follows. (The Default signature is already available)

method IF_EX_ISU_EDM_POD_CUSTOMER~PUT_DATA_TO_SCREEN .

me->if_ex_isu_edm_pod_customer~control_data  = imp_control_data.
me->if_ex_isu_edm_pod_customer~euihead_data  = imp_euihead_data.

endmethod.

 

In GET_DATA_FROM_SCREEN, write logic as follows.

Method IF_EX_ISU_EDM_POD_CUSTOMER~GET_DATA_FROM_SCREEN .

exp_control_data = me->if_ex_isu_edm_pod_customer~control_data.

Move-CORRESPONDING me-> if_ex_isu_edm_pod_customer~euihead_data to exp_euihead_cust_data.

endmethod.

 

Now Save and activate the implementation.

As soon as the object is activated, the custom sub screen is attached with SAP standard Transactions.

Now Execute the TCODE EEDM09 to create a new PoD with address and mobile number as follows

eedm.png

The values for Name and mobile will be created in EUIDHEAD table.

tavl.png

Tips to solve the extension(customer fields) problem in BAPI even though the extension structures are maintained

$
0
0
  • Usually we face problems while using BAPIs for extension. Either extensions are not properly filled or they are filled with some special characters (garbage values).
  • Even if the extension structures for BAPI are properly adjusted I have faced this issue.

 

Consider BAPI BAPI_SALESORDER_CHANGE:

  • Let work area for extension(ls_vbap) have following type.

 

TYPES: BEGIN OF ty_vbap,
vbeln            TYPE vbap-vbeln,
posnr            TYPE vbap-posnr,
zzzlang          TYPE vbap-zzzlang,
zzzdruck         TYPE vbap-zzzdruck

And so on …

 

  • The extension table (t_extension_in) be of type bapiparex.

 

  • BAPI accepts the extension table in character format, so we generally change it like :

 

CLEAR t_extension_in.
t_extension_in-structure     = 'BAPE_VBAP'.


MOVE ls_vbap to t_extension_in+30 .
t_extension_in-valuepart1    = ls_vbap(240).
t_extension_in-valuepart2    = ls_vbap+240.
APPEND t_extension_in.

 

  • This BAPI internally changes the extension table to hexadecimal and then it converts it to character format. So sometimes the original values are wrongly interpreted. So we get garbage values (for eg: ### ) in the customer fields.

 

  • Now if we change the original type of ls_vbap first to hexadecimal and then convert these to character format and then append it to extension table then the internal conversion in BAPI will not give garbage values.

 

  FIELD-SYMBOLS: <fs_x_vbap>     TYPE x,
<fs_c_vbap>     TYPE c.

 

  CLEAR t_extension_in.
t_extension_in-structure     = 'BAPE_VBAP'.

 

* Converting into Hexadecimal value.
ASSIGN ls_vbap        TO <fs_x_vbap>     CASTING.

* Converting Hexadecimal to  char.
ASSIGN <fs_x_vbap> TO <fs_c_vbap>     CASTING.

* Filling Extension table ( excluding first field of 30 char)
t_extension_in+30 = <fs_c_vbap>.

APPEND t_extension_in.

 

  • After these conversion the values in customer fields will appear correctly.

 

A Way of Reading Huge Excel Files

$
0
0

Recently, a colleague of mine had to write an ABAP application with an import functionality for Excel files. I recommended him to use the abap2xlsx package which serves as a fully featured negotiator between the ABAP and the Excel world. In particular, abap2xlsx contains a reader class, ZCL_EXCEL_READER_2007, designed for importing a .xslx file into the ABAP data structures on which the package is based.

 

To my astonishment, the reader dumped with a memory overflow error for a sample Excel file.

  • Yes - with about 28'000 rows and 50 columns it was a large file!
  • And, yes, it is crazy to have a business process with files of these sizes! No discussion about that.

 

On the other hand, the file had a size of 6.5 MB, which was moderate compared to the heap memory limit of 2 GB which had been touched according to the short dump.

 

I was just curious how this memory consumption of a factor way larger than 100 from the original data could be explained.

 

mem_alloc_failed.png

So I wrote a little test report for analyzing the situation with the memory inspector.

 

report zz_test_abap2xlsx_reader.
parameters: p_file type string memory id fil.
at selection-screen on value-request for p_file.   perform get_file(zz_file_io_forms) using p_file.
start-of-selection.   perform start.
* ---
form start.   data: lo_excel_reader type ref to zif_excel_reader,         lo_excel type ref to zcl_excel.   create object lo_excel_reader type zcl_excel_reader_2007.   lo_excel = lo_excel_reader->load_file( p_file ).   break-point.
endform.                    "start

 

Starting the report with one of those large Excel files and stepping through the code with the debugger, I arrive at the place where the worksheet is to be parsed. Everything looks normal. There are some 9 MB allocated for the XSTRING containing the file. At this point, this is the largest object in memory. No problem. The second largest object is the "table of X", the result of the file read process (more or less a redundant copy of the first data - but negligible compared to the 4 GB we are talking about).

 

Peanuts!

 

worksheet1.png

At that point in the debugger, I am just before jumping into the method  get_ixml_from_zip_archive( ). This is a general-purpose method in the reader class, which loads an XML document contained in the zip archive; after loading, it will be parsed with the methods of the IF_IXML family, and a reference to the result XML DOM object  will be passed back to the caller.

 

Here, I am immediately before the call of the parse() method of the if_ixml_parser object. The memory consumption looks still more than modest:

worksheet2.png

Now, with a single step, I am triggering the parse() Method. Processing takes a few minutes until the control is given back to the debugger again. From a memory perspective, the result is amazing:

 

workshhet3.png

With this single method call (which is implemented "by kernel module" and thus cannot be analyzed further with ABAP means), the overall memory consumption  raises to more than 2 GB!

 

Even more amazing: The 2GB cannot be explained from the memory inspector's individual object view :

 

worksheet4.png

 

Here, the top object only requires some 100 MB. This is the decompressed XML worksheet, as produced from the zip loader. From the rest of the 2GB, we see absolutely nothing.

 

So we have to face the fact that the iXML DOM parser in this case requires a factor 20 more memory than the raw document which is to be parsed.

 

Since at this point, nothing could be done (after all, iXML is a kernel component), I looked out for alternatives. My plan was: If we omitted the DOM parsing and instead are satisfied with gripping the element contents from the stream while we're reading it, the memory footprint could be reduced considerably.

 

I could prove that this really is the case, by implementing an alternative parser class, ZCL_XLSX_PARSER, on the base of  the sXML family, in particular of the IF_SXML_READER implementation provided by this family. If you are happy with simply reading the raw data from the file (as we were in our particular case), omitting more functionality like macros, cell styles etc., the class ZCL_XLSX_PARSER will be fully sufficient.

 

At the same place as above, immediately before parsing, the memory in the debugger looks similar: On top, we see the decompressed XML file, allocating about 100 MB as above:

 

worksheet5.png

 

Now I execute the parse_cell_data( ) method.

 

worksheet6.png

For the same file that I tested above, the memory consumption only moderately increased through the parsing: It was 117,707,688 bytes before the call of parse_cell_data( ), and is 141,485,696 bytes afterwards. The difference - about 23 MB - is needed for storing the result (the deep structure ES_EXCEL above, containing internal tables with the cell data). All the figures can be explained by looking at the consumption of the ABAP data alone. We are still far away from the 4GB limit.


Also, the execution speed is better. With the sXML reader, the full reading process needs about 52 seconds. With the iXML reader, only the call of if_ixml_parser->parse() of the same worksheet took 340 seconds.

 

The main difference between IF_IXML_PARSER and IF_SXML_READER is that the latter is only a scanner. It doesn't generate an internal image of the XML file, but only detects the beginning, the end, the attributes and the contents of XML elements during reading the stream. That's it. With IF_SXML, I have to instrument the reader myself if I want to extract data from the reading process. Here is the implementation of the parse_cell_data( ) method, just to give you the idea. If you want to study the whole class, have a look into it here.

 

method parse_cell_data.  data: ls_cell type ty_excel_cell,        lv_number type decfloat34,        lv_time type t,        lv_date type d.
* Main parse loop  while io_reader->node_type ne if_sxml_node=>co_nt_final.    io_reader->next_node( ).    case io_reader->name.      when 'row'.  " A row        check io_reader->node_type = if_sxml_node=>co_nt_element_open.        add 1 to ls_cell-row.        clear: ls_cell-col, ls_cell-ref, ls_cell-type.      when 'c'.  " A column, child of a row        case io_reader->node_type.          when if_sxml_node=>co_nt_element_open.            add 1 to ls_cell-col.            ls_cell-type = get_cell_type( io_reader ).          when if_sxml_node=>co_nt_element_close.            insert ls_cell into table cs_excel-cells.        endcase.      when 'v'.  " A value - may be a reference to the string table        if io_reader->node_type eq if_sxml_node=>co_nt_element_close.          case ls_cell-type.            when gc_cell_datatype-string.              " Referenzindex für Stringtabelle              ls_cell-ref = io_reader->value + 1. " ist 0-basiert in xlsx            when gc_cell_datatype-number.              lv_number = io_reader->value.              append lv_number to cs_excel-numbers.              ls_cell-ref = sy-tabix.            when gc_cell_datatype-date.
* Convert to the ABAP-conformal internal date representation              lv_number = io_reader->value + 693595.              append lv_number to cs_excel-numbers.              ls_cell-ref = sy-tabix.            when gc_cell_datatype-time.
* Convert to the ABAP-conformal internal time representation
* Work with milliseconds, however, for further improvements              lv_number = round( val = 86400 * io_reader->value dec = 3 ).              append lv_number to cs_excel-numbers.              ls_cell-ref = sy-tabix.          endcase.        endif.      when 'is'.  " Inline Strings: Just append them to the stringtab        if io_reader->node_type eq if_sxml_node=>co_nt_element_close.          append io_reader->value to cs_excel-strings.          ls_cell-ref  = sy-tabix.          ls_cell-type = gc_cell_datatype-string.        endif.    endcase.  endwhile.
endmethod.

 

By the way: The code has been written with support of unit tests. The above method, like the rest of the core methods, is covered to 100%. There are only a few unprocessed boundary cases in the unit tests: For example the case of a corrupt zip file (which cannot be unpacked properly). See the ABAP unit test section in the code repository for details.

Case Study On HCM Outbound Interfaces

$
0
0

Overview:

 

     Here i want to share an idea on HCM outbound interfaces which we extensively used in our project.

This blog gives you a basic idea on how we designed our HCM interfaces and please pour in your thoughts as well if you have alternative ideas and suggestions.

 

     In SAP HCM, we often need to send HR Delta data (details of the changes from last run date) to third party interfaces on regular basis. For example, many companies want to secure the services of third party tools like HRG, BENEFEX, PeopleFluent etc to streamline their HCM related jobs. Since the requirements are specific to the third party tool, we often need to develop bespoke interfaces. In our project we have developed a standard template for all the HR interfaces with which we can send ‘Full load’ i.e. current data of all the employees for the initial run to the third party interface and ‘Delta load’ i.e. sends data whenever there is a change in data from the last run date.

 

     This template is flexible and with the minimal code (can be re-used both for ‘Delta Load’ and ‘Full Load’ as well) which we will discuss as we go in to more technical detail and this can be re-used in SAP HCM interfaces which use PNP LDB and require to send data on timely basis.

 

Technical Details:

 

     Two blocks along with the standard PNP screen are added on the selection screen like below (refer screenshot1) with one facilitating the type of Run and the other having Last Run Details (which is not editable) and another option called ‘Override Last Run Date’ to give the flexibility to change the date from which this interface has to consider the changes.

 

  • Advantages of Run Details Block:

 

     Same program can be used to schedule the jobs with the slight changes in variants. Code in the program can be common for both the Delta run and Full load with the exception being Full load runs for whole population while delta load runs only for the employee who has the changes in the desired infotypes since last run date.

 

  • Advantages of Last Run Details block:

 

     Administrator can always have a look at the last run date on which the program was scheduled and the changes are expected only from that date only. Last run date can be changed by overriding it by placing another date in Override last run date field.

 

This block is visible only when the radio button “Delta Load” is selected. (Refer Screenshot 2)

 

     A custom table has to be created to store the values of the interface names and their last run dates which will be updated whenever the program is scheduled. Last run date will be picked up from this table. For the first time, full load has to be scheduled for whole population and the date will be updated.

 

Code Snippets

 

Piece of code which can be used to find the changes in the infotype for delta load when using PNP. If the flag is set, particular employee’s data will appended to final internal table to be sent.

Flag has to be cleared after GET Pernr statement.

 

In HCM, there is a scope of “future dated” records, so for them, the begin date is checked and for normal records, the change date is checked if it falls in specific intervals.

 

  LOOP AT pxxxx WHERE aedtm GE p_last OR begda GE p_last.

    IF  pxxxx-aedtm < pxxxx-begda AND

        pxxxx-begda BETWEEN p_last AND sy-datum.

      gv_flag = abap_true.

      RETURN.

    ELSEIF pxxxx-aedtm BETWEEN p_last AND sy-datum AND pxxxx-begda LE pxxxx-aedtm.

      gv_flag = abap_true.

      RETURN.

    ENDIF.

  ENDLOOP.

 

For Delta Load

 

See if the flag is checked then append the data

 

Wa_final-person = pxxxx-data.

Append wa_final to it_final.

 

For Full Load

 

Wa_final-person = pxxxx-data.

Append wa_final to it_final.

 

So, the code in blue can be re-used for both the processes instead of writing two separate programs. Data can be sent via PI or GI to third party systems.

 

Full load can be scheduled on need basis.

 

Limitations

 

Here, we aren't checking if the specific field which was asked (by third party tool) got changed or not. We are checking if there is a change in whole infotype. The reason is as you guessed performance. As we thought there is no harm in sending the same data again to third party tool as these records can be very few when compared to actual population.

 

We encountered one more problem with this approach. The requirement is to send the records of the employee if there is a change in manager details. As the details of the manager are not stored in any specific table and checking for the change in HRP tables in PNP LDB is time consuming, we went for a separate solution to tackle this problem which i will try to explain in the continuation blog.

 

Please let me know your thoughts to improve this idea or other approaches which you might have implemented to solve these kind of scenarios.

 

    

Hyperlink alias in Smartform

$
0
0

I have searched the forum to find solution for Hyperlink alias in Smartform and found the below link

 

http://scn.sap.com/message/9724092#9724092 - Hyperlink in Smartform

 

http://scn.sap.com/thread/1963802 -  Dynamic Url Smartforms.

 

Later I thought it will be useful for guys like me if I create a blog for the same with screen shots and steps.

 

Create Smartform with text element. Type 'TEST' and click on URL button to activate hyperlink.

 

Step1.JPG

 

Use a callback for the application-specific formatting of the URL address or the URL description. Refer the below program.

 

 

DATA ls_output_options_sf TYPE ssfcompop.
DATA lv_function_module_name TYPE rs38l_fnam.


TYPES: BEGIN OF ty_url,
           name(80),
           url TYPE string,
         END   OF ty_url.

DATA: lt_url TYPE TABLE OF ty_url,
          ls_url TYPE          ty_url.

CALL FUNCTION 'SSF_FUNCTION_MODULE_NAME'
  EXPORTING
    formname           = 'ZHCM_EXP_HYPERLINK'
  IMPORTING
    fm_name            = lv_function_module_name
  EXCEPTIONS
    no_form            = 1
    no_function_module = 2
    others             = 3.

ls_url-name = 'TEST'.
ls_url-url  = 'https://www.google.co.in/'.
APPEND ls_url TO lt_url.


* clear buffer for callback functionality

CALL FUNCTION 'HR_RCF_SF_URL_REFRESH_GT'.

CALL FUNCTION 'HR_RCF_SF_URL_PREPARE_CALLBACK'
  TABLES
    pt_url = lt_url.

ls_output_options_sf-urlcall    = 'HR_RCF_SF_URL_CALLBACK'.

CALL FUNCTION lv_function_module_name
EXPORTING
   OUTPUT_OPTIONS                  = ls_output_options_sf
*   USER_SETTINGS                   = 'X'
* IMPORTING
*   DOCUMENT_OUTPUT_INFO     =
*   JOB_OUTPUT_INFO                 =
*   JOB_OUTPUT_OPTIONS          =
* EXCEPTIONS
*   FORMATTING_ERROR           = 1
*   INTERNAL_ERROR                = 2
*   SEND_ERROR                       = 3
*   USER_CANCELED                 = 4
*   OTHERS                                = 5
          .
IF SY-SUBRC <> 0.
* Implement suitable error handling here
ENDIF.

 

Print Preview Screen for final Output

 

Step2.JPG

 

Final PDF with hyperlink alias

 

Step 3.JPG

 

SAP Note : 622718 - SF Web Forms: Callback for the URL format

 

Comments and suggestions are most appreciated !!


Screen Enhancement For RECN Transaction

$
0
0

This is the common requirement while implementation of REFx Module.

The Requirement is to add the additional fields in RECN transaction. As their are no appropriate BADIs or User Exits available for addressing the requirement, the approach of Business Data Toolset (BDT) is taken up to achieve the same. Following are the steps which would contribute in adding the custom fields.

 

Addition Fields for T-Code RECN

1.jpg


Steps to Add Additional Fields:-


1.   To add fields in master data table go to SE11(ABAP Dictionary) and display table VICNCN. Select the Include CI_VICNCN and add fields in this structure (fields names should start with ZZ or YY).

     2.jpg

2.   Display the Function Group ‘REGC_EXT_EXAMPLE’ in SE80(Object Navigator).

 

    3.jpg

     Copy the Function Group & name the new Funtion Group starting with ‘ZZZZ’( For eg. ZZZZ_REGC_EXT)


     Rename the Old Funcion Modules to New Function Modules(Starting with ‘ZZZZ’)as follow:-

 

     REGC_REGC_EVENT_FMOD2_EXAMPLE            

ZZZZ_REGC_EVENT_FMOD2

     REGC_REGC_PBO_EXAMPLE

ZZZZ_REGC_PBO_Z901

     REGC_REGC_PAI_EXAMPLE

ZZZZ_REGC_PAI_Z901

 

     4.jpg

3.      Publish your application. For publishing call the following transactions for each Application Object.


Transaction

          Object

REBDAO0001

Architectural object

REBDBE0001

Business entity

REBDBU0001

Building

REAJCG0001

Comparative group of apartments

REGC0001

RE contract

RESCPG0001

Participation group

REBDPR0001

Property

REBDRO0001

Rental object

RESCSU0001

Settlement unit

 

       In our case REGC0001,

       Create the New Entry in the table naming Application starting with ‘Z’ (Eg.‘ZT_E’)

 

       5.jpg

4.      Publish your Screen, For Publishing the Screen follow the below steps:-

         (All settings are Client Specific & hence to be transported to into all Clients where required)

   a. Go To Transaction ‘RECACUST’ or follow the path in ‘SPRO’ as below:-


       6.jpg

     b. Create a new FIELD GROUP for your fields by choosing IMG activity 'Dialog -> Screen Layout -> Field Groups -> Field Groups'.

         Choose a number starting with‘7’ (for example 701).

         In the 'General data' area, enter a Description in the 'Description' field for your new fields(Eg Additional Fields in RECN).

          In addition, enter the name of the Function Module from point 2 (ZZZZ_REGC_EVENT_FMOD2) in field 'FM for fld grouping'.

         You can leave the remaining fields empty.

 

         7.jpg

            Double-click 'Field Group -> Fields'  in the dialog structure for the new field group and assign all fields of the sub screen to the field group

            For Example:

            Table: RECN_CONTRACT_CI
            Field name: ZZEBELN
            Input field: Selected (This checkbox must be selected for all fields that are ready for input. In addition, these fields must be defined as input fields in the               Screen Painter. When you save, the system displays a warning message which you can ignore.)

 

           8.jpg

     c. Create a VIEW for your fields by choosing 'Dialog -> Screen Layout -> Views'. For this purpose, copy an existing entry(For example REGC02).

         The name of the new view must begin with ZZZZ(For Eg ZZZZ01) with the text as required(For Eg. ‘Additional Fields in RECN’).

 

         In 'Application'  field, enter the name of your application (In our case ‘ZT_E’).

 

         Enter the program name (SAPLaaaaaa with aaaaaa being the name of your function group.) and the screen number of the subscreen created before:

         Program name: SAPLZZZZ_REGC_EXT

         Screen number: 0901


         In the 'Function module' area, enter the name of your PBO module in field 'Before Output' (For Eg ZZZZ_REGC_PBO_Z901), and enter the name of your              PAI module in the 'After Entry' field (For Eg ZZZZ_REGC_PAI_Z901).


         Leave the values of the remaining fields for the view unchanged.

 

         9.jpg

            In the navigation tree, double-click 'View -> Field Groups'  and enter the field group (701) created before.

            Under 'Further checks’, you do not have to make any entries.

      

         10.jpg


      d. Define a section by choosing 'Dialog -> Screen Layout -> Sections'. The name of the section must begin with ‘ZZZZ’(For Eg, ZZZZ01) with description           as required (For Eg, Customer Tab ) and title Customer Tab The title you enter here will be displayed as a group heading on the screen.

         

          11.jpg

            In the dialog structure, double-click 'Section -> Views' to assign the view created before to the section.

            The last two digits of the line item number have to be different from '00'(For Eg '9000050').


           12.jpg

      e.   Choose 'Dialog -> Screen Layout -> Screens' and define the screen on which the new fields are to be displayed.

            For example, if you want the file number to be displayed on the 'General Data' screen, select line REGC02


           13.jpg

          and double-click 'Screen -> Sections' in the dialog structure.

 

         The line item number that you assign here to the new section should be between the range but it must not end with '00' either.

         (For Eg, if you want the file number to be displayed after the Contract, choose a line item number between 300000 and 400000,

          For Eg 300050 for section ‘ZZZZ01’ ).

 

         14.jpg

 

 

5.      Call Transaction RECN and check whether the added fields respond correctly when you change, display and create a contract.

 

6.      Try saving the data. If data is not getting save in Standard DB Table VICNCN, then follow the below steps.


    a.  Go To SE37(Function Builder) and display the Function Module in our case ‘ZZZZ_REGC_PAI_Z901’ and comment the following code and activate.


*    CALL FUNCTION 'API_RE_CN_GET_DETAIL'
*      EXPORTING
*        io_object  = lo_busobj
*      IMPORTING
*        es_ci_data = recn_contract_ci
*      EXCEPTIONS
*        error      = 1
*        OTHERS     = 2.
*    IF sy-subrc <> 0.
*      MESSAGE ID sy-msgid TYPE 'S' NUMBER sy-msgno
*              DISPLAY LIKE sy-msgty
*              WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
*    ENDIF.


       15.jpg


    b.   Add the following code in Function Group created above PBO Module


             FIELD-SYMBOLS <fs_mix> TYPE any.

             DATA : lv_recnnr TYPE RECNNUMBER.

              ASSIGN ('(SAPLRECA_BDT_APPL_TOOL)GS_OBJECT_INFO-OBJIDENT') TO <fs_mix>.


          IF RECN_CONTRACT_CI-ZZEBELN = GV_ZZEBELNAND RECN_CONTRACT_CI-ZZEBELN IS INITIAL.
       GV_ZZEBELN
= RECN_CONTRACT_CI-ZZEBELN .
       lv_recnnr
= <fs_mix>+5(6).


      
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
      
EXPORTING
        
INPUT        = lv_recnnr
      
IMPORTING
        
OUTPUT        = lv_recnnr .


      
SELECT SINGLE ZZEBELN
                    
FROM VICNCN
                    
INTO RECN_CONTRACT_CI-ZZEBELN
                    
WHERE BUKRS = <fs_mix>+0(4)
                    
AND RECNNR = lv_recnnr.
      ENDIF.

    

              Define GV_ZZEBELN in TOP Include as :-

           DATA  GV_ZZEBELN TYPE RECN_CONTRACT_CI-ZZEBELN.

    

         16.jpg


I hope you find it helpful. Please feel free to make suggestions and/or comments.

With Regards,

Kanak Tendle(KT)

Team Procedural vs Team OO

$
0
0

State Machine Part 2 : Team Procedural vs. Team OO

 

Spider in Canberra Close Up.jpg

 

Contents

 

Introduction

Opening Song by Team Procedural

Code Examples

Various OO and Procedural Musings

Closing Song by Team OO

 

Introduction

 

When Sumanth Kristam wrote the following blog:-

 

http://scn.sap.com/community/abap/blog/2014/01/09/classical-way-to-abap-oo-style-of-coding

 

Little did he realise what a can of worms he was opening up. The SCN programming world went crazy in a debate on the usage of OO programming. It’s pointless me recapping the vast array of comments but in essence “Team OO” were espousing the benefits of OO programming in changing existing programs, and “Team Procedural” were arguing it’s overkill for small programs, and if it’s so good can we have some examples please?

 

Can’t Get There From Here

 

In some of the blogs I have written on this subject I noted the nature of a lot of articles about OO programming I had read. If I can use a beer analogy, it is as if I had been drinking in the same pub for years, lets’ call it the “Procedural Arms” and then I hear about a wonderful new pub called the “Hero of OO”.

 

What is so good about this new pub? I hear myself ask.

 

Back comes the answer – “to get there you leave the current pub, turn left, go up George Street till you come to the Argyle Cut, go left again, under the tunnel, turn right at the end, and at the end of Lower Fort Street is the new, better, pub.

 

I don’t know if you’ve noticed, but that did not actually answer my question. The reply told me HOW to get there, but not what was good about the destination. The OO articles were all like that, it’s good because it’s good, and here is how you write the code technically.

That is why I started my own investigations. I have read a whole bunch of books and articles on the subject – the ones I recommend are “Clean Code” by Robert Martin and “Head First Design patterns” by Elisabeth and Eric Freeman. The latter in particular is chock filled with the sort of examples I was looking for.

 

I Still Haven’t Found What I’m Looking For

 

However all the examples are in Java or C#, which is not surprising as my understanding is that for every ABAP programmer there are twenty million who program in those languages? Nonetheless SAP is my thing, so I started a series of blogs where I translated those examples into ABAP.

 

I was merrily experimenting with assorted design patterns in ABAP, latterly the “state pattern”

 

http://scn.sap.com/community/abap/blog/2013/03/02/oo-design-patterns--decorator--in-abap

 

http://scn.sap.com/community/abap/blog/2013/01/08/domain-specific-language-in-abap

 

http://scn.sap.com/community/abap/blog/2014/01/15/enemy-of-the-state

 

I had got as far as building a bunch of tiny Z classes to implement a state pattern in ABAP, and I was going to see how easy it was to re-use that framework in another context. All OO - naturally. Then all hell broke loose in the Procedural/OO world.

 

So, I thought, I’ll re-write the “Gothic” example in a procedural way, and make sure it does the exact same thing as its OO equivalent. Then we can compare the two programs and that may tell us something.

 

Then (in the next episode) we will pretend the user has some extra requirements, and see how easy it is to change the two sorts of programs. An OO person would say the answer is obvious; OO is going to win this contest hands down. Someone from team procedural would say this example is such a small program, that the overcomplicated OO nonsense will make it more difficult to change.

 

Well, we shall see, will we not?

 

Without further ado, let’s get going. To make this more like an X Factor contest between the two styles I’ll let each programming style sing a song, one at the start one at the end. Since procedural programming predated OO programming it can go first. Most of the audience will be too young to remember “Eagle Rock” but here goes anyway.

 

Opening Song

 

Procedural Rock – Daddy Cool (1971)

 

Daw daw daw, daw daw daw daw,

Daw daw daw, daw daw daw daw,

 

Now listen,
Oh we're steppin' out.
I'm gonna write everything,
Gonna write everything twice and we'll do procedural code.
Oh momma!
Oh you're coding well!
Hmm – global variable,
Well we do it so well when we do procedural code.
Now momma,
Yeah you're so rude!
Why don't you give me a SHARED INCLUDE?
Hmm just give me that and we'll do procedural code.

Chorus:
Hey Hey Hey good old procedural is here to stay,
We have always done it this way,
Doin' procedural rocks.
Oh-oh-oh it’s so fast, OO’s so slow
I'm just crazy 'bout how fast we go
Doin' procedural rocks.

Go momma!
Well you're so keen!
Why don't you give me a FORM ROUTINE?
Just gotta give me that and we'll do procedural code.
Oh baby!
END-OF-SELECTION
You know that gives me, well I don’t like to say,

But anyway, we’re doin’ procedural code.

-Chorus-

-SOLO-

Now listen,
More we're steppin' out.
Yeah, gonna write everything,
Gonna write everything twice and we'll do procedural code.

-Chorus-

-Outro-
Doin’ procedural rocks.
Doin’ procedural rocks.
Doin’ procedural rocks.

 

 

Return to Eden

 

For some years now, I have been forcing myself to do everything in an OO manner, every single program, even ALV reports (more on that later). I have a gut feeling that this will pay off in the long term.

 

However, it’s sad to say, but going back to writing a procedural program after several years of abstaining seems rather like unchaining yourself from a madman and having a hundred ton weight lifted off your shoulders. Header lines, global variables. You know it’s bad for you, but it feels so good.

 

Once again I can write a program from start to finish and it is an indescribable pleasure being able to write a call to a FORM routine, and then double clicking on that call and having the skeleton generated for you, which is something local classes can only dream of. That is why people have resorted to doing everything in Z classes, even for things which are obviously only ever going to be related to the application at hand.

This even gets unlikely support from part of “Clean Code” where Bob Martin says that a program should read like a newspaper i.e. the bit at the top of the source code should say in general terms what the program does, then as you read down it gets more detailed. This is fairly easy in the OO world of Java where you can declare things just before they are used, but ABAP rules enforce declaring everything before you do the implementations, which sort of ruins this.

 

In ABAP procedural code things do tend to look more like a newspaper, with typically a few routines after START-OF-SELECTION describing the various parts of the program, unless the writer had not modularised it at all.

 

Brahms and Code Listing

 

In the links to my blogs above you can see the code for the OO version of the “Gothic” program. In the “Domain Specific Language” one the code is at first glance longer than in the “state” one, because in the “state” one I have moved the bulk of the code into tiny Z classes.

 

Here is the procedural version. I have several comments to make:-

·         I kept in the unit tests, which of course need at least one class to work. This was needed as the test program cannot run on its own; the unit test is the only way to run it.

·         The unit test also proves the procedural code does the exact same thing as the OO code (as the test is exactly the same)

·         This also proves you can do unit tests for procedural programs just as easily as for OO ones. Even if you are dead against moving to OO I would urge you to start using the unit test framework as a bare minimum.

 

*&---------------------------------------------------------------------*
*& Report  Y_GOTHIC_PROCEDURAL
*&
*&---------------------------------------------------------------------*
*& ABAP implementation of Java example program written by Martin Fowler
*& in his book on Domain Specific Languages.
*& I did an OO implementation, now let us try to do the same using
*& procedural programming to contribute to the fierce debate on the
*& internet
*&---------------------------------------------------------------------*
REPORT  y_gothic_procedural.
*--------------------------------------------------------------------*
* Types
*--------------------------------------------------------------------*
TYPES: BEGIN OF g_typ_transitions,
         source_state      
TYPE string,
         event_name        
TYPE string,
         target_state      
TYPE string,
      
END OF g_typ_transitions.

TYPES: BEGIN OF g_typ_abstract_event,
         event_name
TYPE string,
         event_code
TYPE char04,
      
END OF   g_typ_abstract_event.

TYPES: BEGIN OF g_typ_state,
         state
TYPE string,
      
END OF g_typ_state.

TYPES: BEGIN OF g_typ_actions,
         new_state
TYPE string,
         command  
TYPE string,
      
END OF g_typ_actions.
*--------------------------------------------------------------------*
* Lovely Global Variables
*--------------------------------------------------------------------*
DATA: gd_current_state TYPE string VALUE 'idle',
      gt_events       
TYPE STANDARD TABLE OF g_typ_abstract_event,
      gs_events       
TYPE                   g_typ_abstract_event,
      gt_commands     
TYPE STANDARD TABLE OF g_typ_abstract_event,
      gs_commands     
TYPE                   g_typ_abstract_event,
      gt_states       
TYPE STANDARD TABLE OF g_typ_state,
      gs_states       
TYPE                   g_typ_state,
      gt_transitions  
TYPE STANDARD TABLE OF g_typ_transitions,
      gs_transitions  
TYPE                   g_typ_transitions,
      gt_actions      
TYPE STANDARD TABLE OF g_typ_actions,
      gs_actions      
TYPE                   g_typ_actions.

*--------------------------------------------------------------------*
* Macros
*--------------------------------------------------------------------*
DEFINE possible_events_are.
 
clear gs_events.
  gs_events
-event_name = &1.
  gs_events
-event_code = &2.
 
append gs_events to gt_events.
END-OF-DEFINITION.

DEFINE possible_commands_are.
 
clear gs_commands.
  gs_commands
-event_name = &1.
  gs_commands
-event_code = &2.
 
append gs_commands to gt_commands.
END-OF-DEFINITION.

DEFINE possible_states_are.
 
clear gs_states.
  gs_states
-state = &1.
 
append gs_states to gt_states.
END-OF-DEFINITION.

DEFINE state_changes_after_event.
 
clear gs_transitions.
  gs_transitions
-source_state = &1.
  gs_transitions
-event_name   = &2.
  gs_transitions
-target_state = &3.
 
append gs_transitions to gt_transitions.
END-OF-DEFINITION.

DEFINE state_reached_sends_command.
 
clear gs_actions.
  gs_actions
-new_state = &1.
  gs_actions
-command   = &2.
 
append gs_actions to gt_actions.
END-OF-DEFINITION.

*--------------------------------------------------------------------*
* Off we go!
*--------------------------------------------------------------------*
START-OF-SELECTION.
 
PERFORM configure_security_system.
 
PERFORM run_security_system.

*--------------------------------------------------------------------*
* FORM Routines
*--------------------------------------------------------------------*
*&---------------------------------------------------------------------*
*&      Form  CONFIGURE_security_system
*&---------------------------------------------------------------------*
FORM configure_security_system .
* Events
  possible_events_are
:
 
'door_was_closed'       'D1CL',
 
'drawer_was_opened'     'D2OP',
 
'light_was_switched_on' 'L1ON',
 
'door_was_opened'       'D1OP',
 
'panel_was_closed'      'PNCL'.

* Commands
  possible_commands_are
:
 
'unlock_the_panel' 'PNUL',
 
'lock_the_panel'   'PNLK',
 
'lock_the_door'    'D1LK',
 
'unlock_the_door'  'D1UL'.

* States
  possible_states_are
:
 
'idle',
 
'active',
 
'waiting_for_lightLight',
 
'waiting_for_drawer',
 
'panel_is_unlocked'.

* Behaviour
* Idle State
  state_reached_sends_command
: 'idle' 'unlock_the_door',
                              
'idle' 'lock_the_panel'.
  state_changes_after_event
:   'idle' 'door_was_closed' 'active'.

* Active State
  state_changes_after_event
:

'active' 'drawer_was_opened'     'waiting_for_light',
'active' 'light_was_switched_on' 'waiting_for_drawer'.
*
* Waiting for light State
  state_changes_after_event
:

'waiting_for_light' 'light_was_switched_on' 'panel_is_unlocked'.

* Waiting for drawer State
  state_changes_after_event
:

'waiting_for_drawer' 'drawer_was_unlocked' 'panel_is_unlocked'.

* Panel is Unlocked State
  state_reached_sends_command
: 'panel_is_unlocked' 'unlock_the_panel',
                              
'panel_is_unlocked' 'lock_the_door'.
  state_changes_after_event
:   'panel_is_unlocked' 'panel_was_closed' 'idle'.

ENDFORM.                    " CONFIGURE_security_system
*&---------------------------------------------------------------------*
*&      Form  RUN_security_system
*&---------------------------------------------------------------------*
FORM run_security_system .
* Local Variables
 
DATA: ld_code TYPE char04.

 
EXIT."To stop anybody actually running this test program!

 
WHILE ld_code NE 'STOP'.
   
PERFORM poll_for_event CHANGING ld_code.
   
PERFORM handle_event   USING    ld_code.
 
ENDWHILE.

ENDFORM.                    " RUN_security_system
*&---------------------------------------------------------------------*
*&      Form  POLL_FOR_EVENT
*&---------------------------------------------------------------------*
FORM poll_for_event CHANGING pcd_code TYPE char04.
* Code to poll the external system to see what event has occurred
* Most likely a proxy call to PI
ENDFORM.                    " POLL_FOR_EVENT
*&---------------------------------------------------------------------*
*&      Form  HANDLE_EVENT
*&---------------------------------------------------------------------*
FORM handle_event USING pud_event_code TYPE char04.
* Preconditions
 
CHECK pud_event_code IS NOT INITIAL.

 
READ TABLE gt_events INTO gs_events WITH KEY event_code = pud_event_code.

 
CHECK sy-subrc = 0.

 
PERFORM transition USING    gs_events-event_name
                    
CHANGING gd_current_state.

ENDFORM.                    " HANDLE_EVENT
*&---------------------------------------------------------------------*
*&      Form  TRANSITION
*&---------------------------------------------------------------------*
FORM transition  USING    pud_event_name    TYPE string
                
CHANGING pcd_current_state TYPE string.

 
READ TABLE gt_transitions INTO gs_transitions
 
WITH KEY source_state = pcd_current_state
           event_name  
= pud_event_name.

 
CHECK sy-subrc = 0.

  pcd_current_state
= gs_transitions-target_state.

 
LOOP AT gt_actions INTO gs_actions

WHERE new_state = gs_transitions-target_state.


   
READ TABLE gt_commands INTO

gs_commands WITH KEY event_name = gs_actions-command.
   
CHECK sy-subrc = 0.
   
PERFORM send_command_to_ext_system USING gs_commands-event_code.
 
ENDLOOP.

ENDFORM.                    " TRANSITION
*&---------------------------------------------------------------------*
*&      Form  SEND_COMMAND_TO_EXT_SYSTEM
*&---------------------------------------------------------------------*
FORM send_command_to_ext_system USING pud_event_code TYPE char04.
* Code to send out a message to be displayed by an external system
* Most likely a proxy call to PI
ENDFORM.                    " SEND_COMMAND_TO_EXT_SYSTEM
**--------------------------------------------------------------------*
** Local Class Implementations
**--------------------------------------------------------------------*
CLASS lcl_test_class DEFINITION FOR TESTING
   RISK LEVEL HARMLESS
   DURATION SHORT
   FINAL
.
**--------------------------------------------------------------------*
** Miss Grant has a secret compartment in her bedroom that is normally
** locked and concealed. To open it, she has to close the door, then
** open the second drawer in her chest and turn her bedside light
** on—in either order. Once these are done, the secret panel is unlocked
** for her to open.
**--------------------------------------------------------------------*
 
PRIVATE SECTION.
   
METHODS: setup,
            
"IT SHOULD.....................
             open_panel_after_correct_steps
FOR TESTING.

ENDCLASS."Test Class Definition

CLASS lcl_test_class IMPLEMENTATION.

 
METHOD setup.
   
PERFORM configure_security_system.
 
ENDMETHOD.

*--------------------------------------------------------------------*
* Actual Test Methods
*--------------------------------------------------------------------*
 
METHOD open_panel_after_correct_steps.

   
PERFORM given_user_in_room_door_open.

* WHEN user excutes the steps in the correct order
   
PERFORM when_door_gets_closed.
   
PERFORM when_second_drawer_is_opened.
   
PERFORM when_light_switched_on..

* THEN_secret_panel_is_open
    cl_abap_unit_assert
=>assert_equals( act = gd_current_state
                                       
exp = 'panel_is_unlocked' ).

 
ENDMETHOD.

ENDCLASS."Test Class Implementation
*&---------------------------------------------------------------------*
*&      Form  GIVEN_USER_IN_ROOM_DOOR_OPEN
*&---------------------------------------------------------------------*
FORM given_user_in_room_door_open .
 
READ TABLE gt_events INTO gs_events WITH KEY event_name = 'door_was_opened'.
 
PERFORM handle_event USING gs_events-event_code.
ENDFORM.                    " GIVEN_USER_IN_ROOM_DOOR_OPEN
*&---------------------------------------------------------------------*
*&      Form  WHEN_DOOR_GETS_CLOSED
*&---------------------------------------------------------------------*
FORM when_door_gets_closed .
 
READ TABLE gt_events INTO gs_events WITH KEY event_name = 'door_was_closed'.
 
PERFORM handle_event USING gs_events-event_code.
ENDFORM.                    " WHEN_DOOR_GETS_CLOSED
*&---------------------------------------------------------------------*
*&      Form  WHEN_SECOND_DRAWER_IS_OPENED
*&---------------------------------------------------------------------*
FORM when_second_drawer_is_opened .
 
READ TABLE gt_events INTO gs_events WITH KEY event_name = 'drawer_was_opened'.
 
PERFORM handle_event USING gs_events-event_code.
ENDFORM.                    " WHEN_SECOND_DRAWER_IS_OPENED
*&---------------------------------------------------------------------*
*&      Form  WHEN_LIGHT_SWITCHED_ON
*&---------------------------------------------------------------------*
FORM when_light_switched_on .
 
READ TABLE gt_events INTO gs_events WITH KEY event_name = 'light_was_switched_on'.
 
PERFORM handle_event USING gs_events-event_code.
ENDFORM.                    " WHEN_LIGHT_SWITCHED_ON

 

Really I think that is pretty self-explanatory, especially if you read the preceding blogs first. It has to be said that it does read more like a newspaper with each FORM pointing to the more detailed ones that come after it.

 

Naturally if you have not read the preceding blogs you will wonder what in the world this example is trying to achieve – in a nutshell the idea is to try and isolate the part of the program that says what it does from the code that does what it does, and have the “configuration” part in a natural language.

 

The unit test code is pretty much exactly the same length in both programs, as is the configuration section that demonstrates the “domain specific language” example.

 

The code that actually does the logic is a lot shorter in the procedural version. If I could bring myself to start using header lines again then the procedural code would be shorter still, but header lines have been burned out of me after all this time.

 

The funny thing is, in the arguments that rage, a lot of the time people are accused of writing s program using classes and methods that is REALLY a procedural program in wolf’s clothing – static classes and methods, methods that wrap function modules, public variables that are in effect global variables etc….

 

In my example I have gone the other way, and am trying to write a procedural program that mimics OO as much as it can. This could be described as trying to shove a square peg into a round hole.

 

Leaving that aside and getting back to the arguments, the problem with a fair comparison is this – if you do a really complicated example lots of people will get washed away by the complexity of it, or get bogged down in the complexity of the code, ignoring the point you are trying to make.

 

On the B side, the simpler the example, the more striking the percentage difference between the lines of OO code needed to do something and the procedural equivalent. For a small program the procedural version will be a lot shorter. As the complexity increases the sizes will slowly equalise. I am currently writing the most complicated application I have ever written in my life, it’s also by far the biggest in terms of lines of code.

 

I am doing this 100% in an OO fashion, following all the guidelines in the textbooks and from SCN, and I think it has reached the stage where it is smaller than if I had written it procedurally. I think.

 

The acid test of course, is how a program stands up to the non-stop stream of user requests. I have been with my company for 23 years, so I have followed the progress of some custom ABAP programs for a long while. I have found there are two things that can happen to a custom program:-

 

·         It stops getting used

·         It gets a non-stop stream of change requests, forever. Even the simple ALV report type ones.

 

This is why I truly believe that the bulk of programming work is enhancing and changing existing programs as opposed to creating new ones. I don’t think the figure of 90% is an exaggeration, at least in my experience, and I completely refute the argument that if your company introduces a new flavour of ice cream or the government changes a law that somehow magically makes your existing programs badly written.

 

Thus, as far as I can see the OO / Procedural argument stands or falls on how easy it is to change existing programs. Now is the time to jump down a rabbit hole.

 

The OO Empire Strikes Back

 

At the start of the blog it may appear I was bagging OO programming, so let’s have a bit of balance and here are the good things I have found about it.

 

In the examples above in the OO version the external system is encapsulated in its own class, and can be swapped out for a “stub” or whatever you want to call it very easily. The same thing is possible in procedural world I am sure, but it would not be as easy. You would have to have the external system encapsulated in a function module, with some sort of global variable saying if a unit test was running or not. Then the unit test framework would have to call a function module of the same function group to set that global variable, before testing the productive code. That is not quite as elegant as the OO method, ad it means any old program could call the function that said “this is a unit test” and stuff the productive code of the program under test.

 

In addition, if we changed the external system in the OO version you could just pass in a new class with the same interface, I procedural world you would have to do some conditional logic in the function module that handles the external system. I have done this very thing in real life and it is HORRIBLE.

 

Stock Optional

 

What I also like about OO methods is that the parameters can be optional. To be fair that is true of function modules as well, but a function module is NOT a method, even if it looks a bit like one, and naturally FORM routines cannot have optional parameters.

 

This could mean having to have two FORM routines with different signatures, or doing what I used to do and passing in dummy values to the parameters you do not need, usually the CHANGING parameters. That is like in some standard SAP function modules where the TABLES parameter is compulsory, so you have to declare a table, and then include it in the function module call, even if you do not care at all about the contents.

 

I also like saying what the parameters are when calling a subroutine which you have to do in a method call but cannot when calling a FORM routine. This makes it almost impossible to pass in the values in the wrong order. If you make sure each parameter in a FORM routine has a TYPE then the problem is almost solved, unless you have two parameters in a row with the same type, then the caller could get them the wrong way around.

 

My Perfect Recursion

 

In one of my blogs the other day I described the problem of having a function where you had a random number of parameters, and how normally you have to guess the maximum number, and then declare ten optional parameters.

 

Using ABAP OO I created a class which took an instance of itself as an optional parameter, and then “unpacked” itself inside the method. That way you could pass in any number of parameters you wanted.

 

  mo_logger->add_db_read_log_entry(
    io_input_parameter 
= zcl_bc_parameter=>get( value = -slump_group
                 predr 
= zcl_bc_parameter=>get( value = id_consistence_type
                 predr 
= zcl_bc_parameter=>get( value = id_sp_slump ) ) )
    id_database_table  
= 'SLUMP_TABLE'
    io_output_parameter
= zcl_bc_parameter=>get( value = ld_hwr_material
                 predr 
= zcl_bc_parameter=>get( value = ld_initial_dosage
                 predr 
= zcl_bc_parameter=>get( value = ld_hwr_dosage_uom ) ) ) ).

 

Passing an object as a parameter, especially to itself as used in the “decorator” pattern, enabes you to do things which are very difficult in procedural world.

 

A SALV for my Wounds

 

What most people do is ALV reports, so this is where a lot of the examples and questions focus in on. Sadly the simple ALV programs given bloat the code so much it tends to put people off.

 

Here is a different take on this – when I first moved to ECC 6.0 I thought the SALV model was great, as it transformed your internal table into ALV output without having to mess about with the field catalogue.

 

That bonus was offset by the fact that – compared to REUSE_ALV_GRID – doing things like hiding columns and renaming them and sorting and what have you became ten million times more difficult; you had to create objects all over the place.

 

That was problem number one – problem number two was that I wanted to be able to add my own commands to the STAYS programmatically – like I could do with CL_GUI_ALV_GRID – and also make the grid editable if I so desired – like I could with CL_GUI_ALV_GRID.

 

SALV is not supposed to be editable at all I think – see my earlier blogs for how I got round that – but sadly I could do one or the other. I can either add my own commands programmatically to SALV OR make it editable but not both. So if I want both I need to use CL_GUI_ALV_GRID.

 

So, let’s say I have a report that is not editable, with my won commands. So I use SALV. Then a requirement comes along to make it editable – sometimes, for some users. Do I have to rewrite the whole thing totally?

 

REUSE_ALV_GRID, CL_GUI_ALV_GRID and CL_SALV_TABLE all do more or less the exact same thing, but have totally different “interfaces” i.e. how the calling program tells them to sort things or hide fields or what have you. This is a case for the “adapter” pattern.

 

image002.jpg

 

I then have a SALV_VIEW class that implements that interface and a GUI_ALV_GRID class that implements that interface, and at runtime my calling program decides which one to use, the rest of the calling program commands – to sort or hide a column or whatever, are the same in both cases. In the case of the SALV class it has all the million objects as attributes and handles the creation of them itself, leaving the calling program free to just say what it wants the display to look like. I also did a class for REUSE_ALV_GRID just because I could. I stopped short of doing one for WRITE statements but I could have. I should really do a WEB DYNPRO one as well. The point is this is future proof.

 

The Sixth Data Element

 

A common requirement in custom programs is to take a value of, say, a sales organisation (VKORG) and turn this into the text name for display to a human. The problem is that SAP has organised the text tables for every single data element differently, and some like T001W have the text description in the same place as the real table.

 

So, all across the programs you spend time working out how to get the text name of assorted things. I have a ZCL_BC_DATA_ELEMENT class (with a static main method and an abstract method) where you pass in the variable under question e.g. LD_VKORG and get back a string to LD_SALES_ORG_NAME. The class using run time type identification to work out what data element we are dealing with then uses the standard method to try and return the text description. If it finds a non-standard text table, then it looks to see if a subclass of itself exists with the data element in question in its name, and if so, creates an instance of that subclass and calls the main method again, thus getting the text description for a WERKS_D or a LIFNR.

 

In procedural world you could use function modules, but would have to copy more code for each new one you created, and load more into memory (I think) each time the main function module is called the whole group gets loaded into memory, whereas with classes only the static class and it’s subclass would be in memory (I think).

 

Ant Colony Fragile

 

This is probably not relevant at all, but since when has that stopped me.  The other day I was talking about that “anti-fragile” blog I had read on the SCN and was thinking about how to employ that in my daily work.

 

The idea is that every time you are given a change request, you think – can this change happen again, and if so, how can I make it easier to handle next time?

 

In this case the change request was to add a field to a DYNPRO pop-up box screen. That was easy, but then the extra field made the screen bigger, and scroll bars appeared and the user ad to scroll down to the field at the bottom of the screen, making them unhappy.

 

What I should have done was change the code of the CALL SCREEN 1234 STARTING AT X Y EDING AT A B such that it reflected the increased size. I did that and then thought – hang on, what happens when another field is added? Either myself, or another programmer will probably just change the screen and forget about changing the calling statement, especially if the screen is called from multiple places. So I wrote myself a little class to dynamically read the size of the screen from the database (table D020S).

 

METHOD enter_manual_weights.
* Local Variables
   
DATA: ld_end_at_width  TYPE d020s-noco,
          ld_end_at_height
TYPE d020s-noli.

    zcl_bc_screen_size
=>get_pop_up_end_at_points(
     
EXPORTING
        id_program      
= sy-repid    " Program
        id_screen       
= '0700'      " Current Screen Number
     
IMPORTING
        ed_end_at_height
= ld_end_at_height   " End at Height
        ed_end_at_width 
= ld_end_at_width ). " End at Width

   
CALL SCREEN '0700'  "Enter Manual Weights
        
STARTING AT 25 06 "Same as Popup to Confirm
         ENDING  
AT ld_end_at_width ld_end_at_height.

 
ENDMETHOD."Enter Manual Weights

 

The aim of the game is that every change you make should make the next change easier, which is the opposite of what we are used to, but as can be seen from this example, more than possible. That is not a good entry in the procedural vs ABAP debate, as a function module could do the exact same thing, but I think this “anti-fragile” thing is just as important as unit tests, so I put it in anyway.

 

In the next exciting episode….

 

I think that is enough random burbling for the time being, in the next episode I will invent some new user requirements for my “Gothic” program and see if the OO version or the procedural version is easier to change.

 

Incidentally, that picture of the spider at the start of the blog I took last weekend, when I was at my friend’s house near Canberra. That is not a particularly dangerous spider (by Australian standards) but it was big enough to bother my wife, especially when my friend poked it. She didn’t realise he is a lawyer, so deadly spiders are scared of him, sadly they are not scared of computer programmers.

 

To finish off this instalment it is the turn of OO programming to sing a song….

 

Closing Song

 

OO Just A Little Bit – 1996 – Original Lyrics S.Tauber/S.Rodway

 

In the style of “The Wurzels”

 

Wurp idle didle do,

Wurp idle didle do,

You're my code
You're the sweetest thing
Don't rot away
Don't rot away

Every change makes me hate the users
Don’t Call Us
We’ll Call You

Is I wrong, to encapsulate
All me methods,

Be that pathetic?
I can't hide all they patterns of state
(Oink!)
All my principles are open and closed

 

Baa!

CHORUS:

O! O!
Just a little bit
O! O!
A little bit more
O! O!
Just a little bit
Patterns by they Gang of Four

O! O!
Just a little bit
O! O!
Be just the job
O! O!
Just a little bit
Read they books by Uncle Bob

 

Moo!

Working out
With code that’s legacy
Inheritance

It makes me dance

But tonight that guy Feathers say
Let me refactor, whilst on me tractor
How can I prove my love for U  (ML)

Baby please, the subclass that I need
Is the Liskov substitution from hell

Ee-aww!

CHORUS:

O! O!
Just a little bit
O! O!
I feels the force
O! O!
Just a little bit
I loves that girl called Polly Morph
O! O!
Just a little bit
O! O!
A little bit more
O! O!
Just a little bit
Patterns by they Gang of Four

O! O!

O! O!

ABAP - ALV Context menu + keep row selection after a filter is applied

$
0
0

Original of this article comes from oprsteny.com blog site.

 

Standard functionality of an ALV grid offers row selection by clicking on the row headers.

You can also use Ctrl or Shift keys to do mutliple row selection.

 

Drawback of this is when you need to select some rows and apply some filters at the same time.

Setting or deleting filter (or any operation which does grid refresh) clears the row selection.

If you need to keep the row selection together with functionality of filter (and others), you can follow my little suggestion.

 

The idea is simple - extend the selection model with separate column to hold the selected/not-selected information.

It can be interpreted as a checkbox for example.

 

Selection of rows can now be made either by clicking on check boxes OR by clicking on the row headers + applying a context menu action.

 

Here I'll provide a sample code how to achieve this.

 

Prerequisites:

  • I have a Z-program where I implement local class handling the ALV
  • In SE80 I created screen 0100 for this program

 

Note:

  • I'll show a demo for simple field catalog (material and plant columns)
  • Data loading is faked (hard coded values) to limit complexity of the code

 

 

Structure of data in ALV grid:

 

TYPES:  BEGIN OF gty_data,    selected TYPE char1,    matnr TYPE matnr,    plant TYPE werks_d,  END OF gty_data.

 

 

Global data and PBO code for screen 0100:

 

DATA:  gr_grid TYPE REF TO lcl_demo.

START-OF-SELECTION.
  CALL SCREEN 100.

MODULE pbo_0100 OUTPUT.
  CREATE OBJECT gr_grid.  gr_grid->show_grid( ).
ENDMODULE.

 

 

Class definition:

 

CLASS lcl_demo DEFINITION.  PUBLIC SECTION.    METHODS show_grid.  PRIVATE SECTION.    DATA:      mt_fieldcat TYPE lvc_t_fcat,              mo_data_grid TYPE REF TO cl_gui_alv_grid,      mt_data TYPE STANDARD TABLE OF gty_data WITH KEY matnr.    CONSTANTS:
*     Functions used in the context menu      BEGIN OF mc_functions,        select_rows              TYPE ui_func VALUE 'SELECT_ROWS',        unselect_rows            TYPE ui_func VALUE 'UNSELECT_ROWS',      END OF mc_functions.

*   Own method for handling ALV context menu request    METHODS my_ctx_menu_request_handler      " CONTEXT_MENU_REQUEST       FOR EVENT context_menu_request OF cl_gui_alv_grid         IMPORTING           e_object.

*   Own method for handling user command sent from context menu    METHODS my_user_command_handler                 " USER_COMMAND       FOR EVENT user_command OF cl_gui_alv_grid         IMPORTING           e_ucomm.    METHODS set_rows_selected      IMPORTING is_selected TYPE abap_bool.    METHODS build_fieldcat.    METHODS load_data.
ENDCLASS.

 

 

Implementation of the class:

 

CLASS lcl_demo IMPLEMENTATION.  METHOD build_fieldcat.    FIELD-SYMBOLS:      <fs_fcat> TYPE lvc_s_fcat.

*   Checkbox column used to determine if a row is selected    APPEND INITIAL LINE TO mt_fieldcat ASSIGNING <fs_fcat>.    <fs_fcat>-fieldname = 'SELECTED'.    <fs_fcat>-checkbox  = abap_true.    <fs_fcat>-edit      = abap_true.    <fs_fcat>-scrtext_s = 'Selected'.    <fs_fcat>-outputlen = 5.    APPEND INITIAL LINE TO mt_fieldcat ASSIGNING <fs_fcat>.    <fs_fcat>-fieldname = 'MATNR'.    <fs_fcat>-scrtext_s = 'Material'.    APPEND INITIAL LINE TO mt_fieldcat ASSIGNING <fs_fcat>.    <fs_fcat>-fieldname = 'PLANT'.    <fs_fcat>-scrtext_s = 'Plant'.  ENDMETHOD.                    "build_fieldcat

* Method to load data for ALV grid
  METHOD load_data.    FIELD-SYMBOLS:      <fs_data> TYPE gty_data.    APPEND INITIAL LINE TO mt_data ASSIGNING <fs_data>.    <fs_data>-matnr = 123456.    <fs_data>-plant = 'P001'.    APPEND INITIAL LINE TO mt_data ASSIGNING <fs_data>.    <fs_data>-matnr = 654321.    <fs_data>-plant = 'P999'.  ENDMETHOD.                    "load_data  METHOD show_grid.    DATA:      ls_layout   TYPE lvc_s_layo.    IF mo_data_grid IS INITIAL.
*     Create the ALV grid object using the whole screen
*     as container      CREATE OBJECT mo_data_grid        EXPORTING          i_parent      = cl_gui_container=>screen0          i_appl_events = abap_true.      ls_layout-sel_mode = 'A'.      ls_layout-no_rowmark = abap_false.      me->build_fieldcat( ).      me->load_data( ).

*     Register handler for context menu request      SET HANDLER my_ctx_menu_request_handler FOR mo_data_grid.

*     Register handler for user action in context menu      SET HANDLER my_user_command_handler FOR mo_data_grid.

*     Display the ALV grid      mo_data_grid->set_table_for_first_display(        EXPORTING          is_layout             = ls_layout        CHANGING          it_fieldcatalog       = mt_fieldcat          it_outtab             = mt_data ).    ENDIF.  ENDMETHOD.                    "show_grid

* Implementation of the context menu request
  METHOD my_ctx_menu_request_handler.    DATA:      ls_row    TYPE lvc_s_row,      ls_col    TYPE lvc_s_col.
*   Get cell which was clicked    CALL METHOD mo_data_grid->get_current_cell      IMPORTING        es_row_id = ls_row        es_col_id = ls_col.

*   Add ctx-menu items only if clicked on row header where
*   FIELDNAME is initial    CHECK ls_col-fieldname IS INITIAL.

*   Adding custom functions    CALL METHOD e_object->add_function      EXPORTING        fcode = mc_functions-select_rows        text  = 'Select highlighted rows'.    CALL METHOD e_object->add_function      EXPORTING        fcode = mc_functions-unselect_rows        text  = 'Unselect highlighted rows'.  ENDMETHOD.                    "my_context_menu_request_handler

* Set the selected rows checked or unchecked 
* modifies the first column
  METHOD set_rows_selected.    DATA:    lt_selected_rowids TYPE lvc_t_roid.    FIELD-SYMBOLS:      <fs_data> TYPE gty_data,      <fs_selrow> TYPE lvc_s_roid.

*   Get selected rows (using row headers)    CALL METHOD mo_data_grid->get_selected_rows      IMPORTING        et_row_no = lt_selected_rowids.

*   Modify ALV grid data - first column    LOOP AT lt_selected_rowids ASSIGNING <fs_selrow>.      READ TABLE mt_data ASSIGNING <fs_data> INDEX <fs_selrow>-row_id.      CHECK sy-subrc = 0.      <fs_data>-selected = is_selected.    ENDLOOP.  ENDMETHOD.                    "set_rows_selected

* Method to handle response on user actions in context menu
  METHOD my_user_command_handler.    CASE e_ucomm.      WHEN mc_functions-select_rows.        set_rows_selected( abap_true ).      WHEN mc_functions-unselect_rows.        set_rows_selected( abap_false ).    ENDCASE.    IF mo_data_grid IS NOT INITIAL.      mo_data_grid->refresh_table_display( ).    ENDIF.  ENDMETHOD.                    "my_user_command_handler
ENDCLASS.

 

 

Output:

 

ALV Context menu

ALV custom toolbar button to import data from clipboard

$
0
0

Original blog post comes from oprsteny.com

 

In this code example I'd like to show how to add custom button to your ALV toolbar.

This custom button will import data from clipboard into the ALV grid.

It can solve the issue with classic Ctrl+C and Ctrl+V where there's a problem that new rows are NOT created in the ALV grid automatically - this will be solved by the extra toolbar button

 

 

I have created a Z-program and screen 0100 to demonstrate the functionality.

 

Here comes the definition of a simple data structure used in our ALV grid:

 

TYPES:  BEGIN OF gty_data,    matnr TYPE matnr,    plant TYPE werks_d,  END OF gty_data.

 

 

Definition of the LCL_DEMO class :

 

CLASS lcl_demo DEFINITION.  PUBLIC SECTION.    METHODS show_grid.  PRIVATE SECTION.    DATA:      mt_fieldcat TYPE lvc_t_fcat,      mo_data_grid TYPE REF TO cl_gui_alv_grid,      mt_data TYPE STANDARD TABLE OF gty_data WITH KEY matnr.    CONSTANTS:
*     Custom user functions encapsulated in private class constant      BEGIN OF mc_functions,        import_clipboard TYPE ui_func VALUE 'IMP_CLIPBOARD',      END OF mc_functions.    METHODS my_toolbar_handler      " TOOLBAR       FOR EVENT toolbar OF cl_gui_alv_grid         IMPORTING           e_object           e_interactive.    METHODS my_user_command_handler    " USER_COMMAND       FOR EVENT user_command OF cl_gui_alv_grid         IMPORTING           e_ucomm.    METHODS import_clipboard.    METHODS build_fieldcat.
ENDCLASS.



Here follows implementation of the class:

 

CLASS lcl_demo IMPLEMENTATION.

* Create field catalog for our two fields
  METHOD build_fieldcat.    FIELD-SYMBOLS:      <fs_fcat> TYPE lvc_s_fcat.    APPEND INITIAL LINE TO mt_fieldcat ASSIGNING <fs_fcat>.    <fs_fcat>-fieldname = 'MATNR'.    <fs_fcat>-scrtext_s = 'Material'.    APPEND INITIAL LINE TO mt_fieldcat ASSIGNING <fs_fcat>.    <fs_fcat>-fieldname = 'PLANT'.    <fs_fcat>-scrtext_s = 'Plant'.  ENDMETHOD.                    "build_fieldcat

 

 

There are actually several types of objects that can be added to the toolbar:

 

ValueConstantMeaning
0cntb_btype_buttonButton (normal)
1cntb_btype_dropdownPushbutton with menu
2cntb_btype_menuMenu
3cntb_btype_sepSeperator
4cntb_btype_groupPushbutton group
5cntb_btype_checkCheckbox
6Menu entry

 

 

Method which adds the custom button to ALV toolbar:

 

  METHOD my_toolbar_handler.    data:      ls_button type stb_button.    ls_button-butn_type = 0. "Button    ls_button-function = mc_functions-import_clipboard.    ls_button-icon = '@48@'. "Import icon    ls_button-text = 'Import Clipboard'.    INSERT ls_button into e_object->mt_toolbar INDEX 1.  ENDMETHOD.

 

 

Method which handles user-click on the custom toolbar button:

 

  METHOD my_user_command_handler.    CASE e_ucomm.      WHEN mc_functions-import_clipboard.        me->import_clipboard( ).    ENDCASE.

*   We need to refresh grid with updated data    IF mo_data_grid IS NOT INITIAL.      mo_data_grid->refresh_table_display( ).    ENDIF.  ENDMETHOD.

 

 

A method for clipboard import:

 

  METHOD import_clipboard.    DATA:      ls_data type gty_data,      lt_clipdata TYPE STANDARD TABLE OF char255,      ls_clipdata type char255,      lt_record TYPE STANDARD TABLE OF char255,      ls_record type char255,      lv_clip_len TYPE i.    CONSTANTS: c_tab  TYPE c VALUE cl_bcs_convert=>gc_tab.    cl_gui_frontend_services=>clipboard_import(      IMPORTING         data                 = lt_clipdata         length               = lv_clip_len       EXCEPTIONS         cntl_error           = 1         error_no_gui         = 2         not_supported_by_gui = 3         OTHERS               = 4 ).    IF sy-subrc NE 0.      MESSAGE 'Error while importing data from clipboard' TYPE 'I'.      RETURN.    ENDIF.    LOOP AT lt_clipdata INTO ls_clipdata.
*     Split data to respective fields      SPLIT ls_clipdata AT c_tab        INTO TABLE lt_record.

*     Populate data into the ls_data structure      CLEAR ls_data.      "reading MATNR      READ TABLE lt_record INTO ls_record INDEX 1.      IF sy-subrc = 0.        ls_data-matnr = ls_record.      ENDIF.      "reading PLANT      READ TABLE lt_record INTO ls_record INDEX 2.      IF sy-subrc = 0.        ls_data-plant = ls_record.      ENDIF.

*     Populate the ALV data table      IF ls_data IS NOT INITIAL.        APPEND ls_data TO mt_data.      ENDIF.    ENDLOOP.  ENDMETHOD.

 

 

And the final public method to display the ALV grid:

 

  METHOD show_grid.    DATA:      ls_layout   TYPE lvc_s_layo.    IF mo_data_grid IS INITIAL.      CREATE OBJECT mo_data_grid        EXPORTING          i_parent      = cl_gui_container=>screen0          i_appl_events = abap_true.      ls_layout-sel_mode = 'A'.      ls_layout-no_rowmark = abap_false.      build_fieldcat( ).

*     !!! Handlers registration !!!      SET HANDLER my_toolbar_handler FOR mo_data_grid.      SET HANDLER my_user_command_handler FOR mo_data_grid.      mo_data_grid->set_table_for_first_display(        EXPORTING          is_layout             = ls_layout        CHANGING          it_fieldcatalog       = mt_fieldcat          it_outtab             = mt_data ).    ENDIF.  ENDMETHOD.

 

 

The report main program is now quite simple:

 

DATA:  gr_grid TYPE REF TO lcl_demo.

START-OF-SELECTION.
  CALL SCREEN 100.

MODULE pbo OUTPUT.
  CREATE OBJECT gr_grid.  gr_grid->show_grid( ).
ENDMODULE.

 

 

To show you a real example...

I prepared few lines in Excel:

Excel data

 

After running the ABAP report (code above) I got this screen:

Custom toolbar button demo

 

When I pressed the Import Clipboard button I got the following:

Custom toolbar button demo - Imported data

Sending Mail from SAP to other Platform with HTML format.

$
0
0

Hi Friends,

                        Am happy to share few things, what i have learned from ABAP and also it is My first blog too.

Below I have mentioned how to send mail from SAP to some other platforms  in HTML format. Here there is no need to  know about HTML language. If anyone  aware of HTML means please skip the below  3 steps.

Step1:

       Please create the format of output in Microsoft Word .

 

 

This is the format I created.

 

 

 

Step 2:

    Please Save it as type web Page Shown in Below Screen Shots,

 

 

Step 3:

   Just do open with note Pad. It will generates the HTML coding for our designed output.

 

It shows many unwanted coding please select the color code,column width , size from generated HTML Coding.

 

 

 

Step 4 :

Please create the ABAP program as your requirements..

Below Just am mentioning my coding.

 

DATA : BEGIN OF wa,
SNO
(8),
MATNR
TYPE makt-MATNR,
MAKTX 
TYPE makt-MAKTX,
MAKTg 
TYPE makt-MAKTG,
END OF wa.
data : itab like wa OCCURS 0 .
*DATA : wa LIKE LINE OF itab.
data  color(10).
DATA : i TYPE i.

data: send_request       type ref to cl_bcs,
document          
type ref to cl_document_bcs,
sender            
type ref to cl_sapuser_bcs,
recipient         
type ref to if_recipient_bcs,
xirecipient       
type ref to cl_xi_mail_recipient,
bcs_exception     
type ref to cx_bcs,
sent_to_all       
type os_boolean,
uname             
type xubname,
state             
type alautoemail,
r3syst            
type sysysid,
dest              
type ad_symbdst,
client             type ad_umand,
nrecip            
type i,
t_hex             
type solix_tab,
t_hex2             
type solix_tab.
data:  begin of html_data,
line        type string ,
end of html_data.
data:  xhtml_string       type xstring,
p_ttext2           
type bcsy_text,
p_ttext           
like standard table of html_data,
sep_mail.

data:
subject
type so_obj_des,
e_mail
(50).
data  flag.
data  plant type pa0001-werks.
data  : begin of wa_email,
e_mail
like adr6-smtp_addr,
end of wa_email.
data : itab_email like wa_email occurs 0.

data  lf_sender type ref to if_sender_bcs.

SELECT * FROM makt Into CORRESPONDING FIELDS OF TABLE itab UP TO 10 ROWS.




wa_email
-e_mail = 'abc@xyz.in'. “ Designation Address ,here we can can add N number of mail ID to Send.
append wa_email to itab_email.



concatenate '<html>'
'' into html_data-line separated by space.
append html_data to p_ttext.

concatenate '<h3><span style="font-size:20.0pt;line-height:115%;color:#548DD4">         '
'      The following materials are present in MAKT table '
'</span></h3>' into html_data-line separated by space.
append html_data to p_ttext.

concatenate '<table cellspacing=0 cellpadding=0 style="border-color:WHITE">' ''
into html_data-line separated by space.
append html_data to p_ttext.
concatenate  '<tr> <td width=50 valign=top style="color:white;'
'background-color:#92CDDC;border:solid WHITE 1.0pt;">'
'<b> S.no</b></td>' ''
into html_data-line separated by space.
append html_data to p_ttext.

concatenate  '<td width=50 valign=top style="color:white;'
'background-color:#92CDDC;border:solid WHITE 1.0pt;">'
'<b>Material</b></td>' ''
into html_data-line separated by space.
append html_data to p_ttext.

concatenate  '<td width=250 valign=top style="color:white;'
'background-color:#92CDDC;border:solid WHITE 1.0pt;">'
'<b>Desc </b></td>' ''
into html_data-line separated by space.
append html_data to p_ttext.

concatenate  '<td width=250 valign=top style="color:white;'
'background-color:#92CDDC;border:solid WHITE 1.0pt;">'
'<b>DESC in Captial </b></td>' ''
into html_data-line separated by space.
append html_data to p_ttext.

i = 0.
loop at itab into wa.

i = i + 1.
wa
-sno = i .

CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT'
EXPORTING
INPUT         = wa-matnr
IMPORTING
OUTPUT        = wa-matnr
.

if sy-tabix mod 2 = 1.
color = '#B6DDE8'.
else.
color = '#DAEEF3'.
endif.

concatenate '<tr><td width=50 valign=top style="'
'background-color:' color'  ;border:solid WHITE 1.0pt;">' '' into html_data-line separated by space.
append html_data to p_ttext.
concatenate ' '
wa
-sno '</td>' into html_data-line separated by space.
append html_data to p_ttext.

concatenate ' <td width=50 valign=top style=" '
'background-color:' color' ;border:solid WHITE 1.0pt;">'
into html_data-line separated by space.
append html_data to p_ttext.
concatenate ' '
wa
-matnr '</td>' into html_data-line separated by space.
append html_data to p_ttext.

concatenate ' <td width=250 valign=top style=" '
'background-color: ' color' ;border:solid WHITE 1.0pt;">'
into html_data-line separated by space.
append html_data to p_ttext.
concatenate ''
wa
-maktx '</td>' into html_data-line separated by space.
append html_data to p_ttext.

concatenate ' <td width=250 valign=top style=" '
'background-color:'  color ';border:solid WHITE 1.0pt;">'
into html_data-line separated by space.
append html_data to p_ttext.
concatenate ''
wa
-maktg '</td>' into html_data-line separated by space.
append html_data to p_ttext.

endloop.

concatenate '</table><p class=MsoNormal> </p>'
'</div></html>' into html_data-line separated by space.
append html_data to p_ttext.
move '<strong>Regards,</strong><br/>' to html_data-line.
append html_data to p_ttext.
move '<strong>  MM TEAM,</strong>' to html_data-line.
append html_data to p_ttext.

loop at itab_email into wa_email.
loop at p_ttext into html_data.

call function 'SCMS_STRING_TO_XSTRING'
exporting
text     = html_data-line
importing
buffer   = xhtml_string
exceptions
failed  
= 1
others   = 2.

call function 'SCMS_XSTRING_TO_BINARY'
exporting
buffer                = xhtml_string
tables
binary_tab           
= t_hex.

append lines of t_hex to t_hex2.

endloop.

try.

*--- create and set document
document
= cl_document_bcs=>create_document(
i_type         
= 'HTM'  "see table TSOTD
i_subject      
= 'Material '
i_importance   
= '1' "1 = high,5 = normal,9 = low
"       i_sensitivity   = '0'  "standard
"       i_text          = p_ttext
i_hex          
= t_hex2
*                     I_HEADER        =
*                     I_SENDER        =
).
*          clear: t_hex2,t_hex2[].
*--- prevent dumps due to empty document
if document is initial.
exit.
endif.
*--- create persistent send request
send_request
= cl_bcs=>create_persistent( ).

*--- add document to send request
send_request
->set_document( document ).

*--- set sender
*          sender = cl_sapuser_bcs=>create( 'DDIC' ).
try .

lf_sender
= cl_cam_address_bcs=>create_internet_address(
i_address_string
= '123@sap.com' “ with help of in this mail only it will send the mail

i_address_name  
= 'DDIC' ).  "Display name
send_request
->set_sender( sender ).

catch cx_bcs into bcs_exception..

endtry.

send_request
->set_sender( lf_sender ).


recipient
= cl_cam_address_bcs=>create_internet_address(
i_address_string    
= wa_email-e_mail   ).
*i_address_NAME     = 'DDIC' ).


send_request
->add_recipient( i_recipient = recipient
i_express  
= 'X' ) .

*--- send document
sent_to_all
= send_request->send( ).
if sent_to_all <> 'X'.  "not ok
exit.
endif.

commit work.   "!!!
*---exception handling
catch cx_bcs into bcs_exception.
write bcs_exception->error_type.
exit.
endtry.
*    endloop.
clear: html_data ,xhtml_string, t_hex[], t_hex2[].
endloop.

Like this you can code as per your requirements. .

 

 

 

 

 

Here am just created the simple basic layout . we can create different format and different color coding .

We can execute this as background and foreground.

Change the data without table maintenance in non-production system

$
0
0

Try this at your own risk!!!

 

The idea of this blog is to share the solution to a common problem faced by many functional consultants while testing. While we need to change the data of a table which does not have a SM30 maintenance view. We can change the data in almost any table either a custom table or a standard table depending upon the admin settings, we just need some basic ABAP knowledge to do this.

 

  1. SAP has provided a standard function module SE16N_INTERFACE, we can call this FM to edit any table. This FM calls the SE16N transaction in the edit mode.
  2. Execute this FM in SE37 and input the table name and set 'I_EDIT' and 'I_SAPEDIT' fields as 'X' and execute.
    1.JPG
  3. Click on the insert/Delete icon to add to delete any row. We can also modify the existing data.
    2.JPG
    3.JPG
  4. While modifying the data, we can restrict the number of entries, restrict the data on the selection criteria and can display the desired fields instead of the displaying the whole table.

 

We must make sure the data whether the data is correct or not while dealing with standard SAP tables, as it may lead to inconsistency in the system.

This FM will work in most of the non-production systems but depending upon the system settings it might not work in few systems.

ABAP Language News for Release 7.40, SP05

$
0
0


Release news for a support package? Yes. My last blog ABAP Language News for Release 7.40 was about 7.40, SP02.  SP02 came with kernel release 740. 7.40, SP05 is a bundled support package that comes with a new kernel release 741.  And a new kernel release means new ABAP language features.

 

Read this blog where I cover the most important language news for SP05 to learn why it makes sense to get 7.40, SP05 as soon as possible!

 

Internal Tables

 

MOVE-CORRESPONDING for Internal Tables

 

You can use MOVE-CORRESPONDING not only for structures but also for internal tables now. Components of the same name are are assigned row by row. New additions EXPANDING NESTED TABLES and KEEPING TARGET LINES allow to resolve tabular components of structures and to append lines instead of overwriting existing lines.


Example

 

MOVE-CORRESPONDING itab1 TO itab2 EXPANDING NESTED TABLES

                                  KEEPING TARGET LINES.

 

More About

 

http://help.sap.com/abapdocu_740/en/index.htm?file=abapmove-corresponding.htm

 

 

 

Expressions and Functions

 

LET Expressions in Constructor Expressions

 

New LET expressions as sub expressions of constructor expressions allow you to define local variables or field symbols as auxiliary fields of constructor expressions.

 

Example

 

LET expression in a table construction using the VALUE operator.

 

cl_demo_output=>new( )->write(

  VALUE text( LET it = `be` IN

                 ( |To { it } is to do|          )

                 ( |To { it }, or not to { it }| )

                 ( |To do is to { it }|          )

                 ( |Do { it } do { it } do|      ) ) )->display( ).

 

More About

 

http://help.sap.com/abapdocu_740/en/index.htm?file=abaplet.htm

 

 

 

CORRESPONDING Operator

 

The new constructor operator CORRESPONDING allows to execute  a "MOVE-CORRESPONDING" for structures or internal tables at operand positions. Besides assigning components of the same name you can define your own mapping rules.

 

Example

 

struct2 = CORRESPONDING #( struct1 MAPPING b4 = a3 ).

 

More About

 

http://help.sap.com/abapdocu_740/en/index.htm?file=abenconstructor_expr_corresponding.htm

 

 

 

Table Comprehensions


A new FOR sub expression for constructor expressions with operators NEW and VALUE allows to read existing internal tables and to construct new tabular contents from the lines read.

 

Example

 

Construction of an internal table itab2 from lines and columns of an internal table itab1. You can of course also use the CORRESPONDING operator to construct the lines.

 

DATA(itab2) = VALUE t_itab2( FOR wa IN itab1 WHERE ( col1 < 30 )

                             ( col1 = wa-col2 col2 = wa-col3 ) ).

 

More About

 

http://help.sap.com/abapdocu_740/en/index.htm?file=abentable_comprehensions.htm

 

 

Meshes

 

This one is a little bit tricky. Meshes are special structures whose components are internal tables, which can be linked to

each other using associations. Associations are evaluated by specifying mesh paths in suitable expressions and statements.The full power of meshes will become more clear in the monent when associations will be supported by Open SQL for database views (CDS views, see below) in the future.


Example

 

A mesh flights is declared from a mesh type t_flights. In t_flights you have tabular components as so called mesh nodes that are linked by associations. A structured data object root  is constructed to serve as the start line for the following LOOP over a mesh path. The results are lines from sflight that are found by following the mesh path evaluating the associations between its mesh nodes.

 

TYPES:
  t_scarr   TYPE SORTED TABLE OF scarr
            WITH UNIQUE KEY carrid,
  t_spfli   TYPE SORTED TABLE OF spfli
            WITH UNIQUE KEY carrid connid,
  t_sflight TYPE SORTED TABLE OF sflight
            WITH UNIQUE KEY carrid connid fldate.

 

TYPES:
  BEGIN OF MESH t_flights,
    scarr   TYPE t_scarr
      ASSOCIATION to_spfli TO spfli
               ON carrid = carrid USING KEY primary_key,
    spfli   TYPE t_spfli
      ASSOCIATION to_sflight TO sflight
               ON carrid = carrid AND
                  connid = connid USING KEY primary_key,
    sflight TYPE t_sflight,
  END OF MESH t_flights.

 

DATA:
  flights TYPE t_flights.


...

 

DATA(root) = flights-scarr[ carrname = 'United Airlines' ].

 

LOOP AT
  flights-scarr\to_spfli[ root ]\to_sflight[ ]
    INTO DATA(wa).
  ...
ENDLOOP.


More About

 

http://help.sap.com/abapdocu_740/en/index.htm?file=abenabap_meshes.htm

 

 

Open SQL

 

New Syntax

 

From 7.40, SP05

 

  • Lists in Open SQL statements can and should be separated by a comma.
  • Host variables in Open SQL statements can and should be escaped by a @.

 

Only then you will be able to use other new functions that are based on a new SQL parser in the ABAP kernel.

 

Example

 

SELECT carrid, connid, fldate

       FROM sflight

       INTO CORRESPONDING FIELDS OF TABLE @sflight_tab

       WHERE carrid = @carrier AND

             connid = @connection

       ORDER BY carrid, connid.

 

More About

 

http://help.sap.com/abapdocu_740/en/index.htm?file=ABENNEWS-740_SP05-OPEN_SQL.htm

 

 

SQL Expressions

 

Yo can use SQL expressions in a column list behind SELECT. The result of such an expression is calculated on the database (code push down!) and written into the respective columns of the result set. Operands can be data base columns or host variables.

 

Possible expressions are

 

  • elementary values
  • arithmetic expressions
  • arithmetic functions abs, ceil, floor, div, mod
  • castings with cast
  • string concatenations with &&
  • coalesce
  • case

 

Expressions can be mixed and proritized with parentheses.

 

Examples

 

SELECT id, num1, num2,

       cast( num1 AS fltp ) / cast( num2 AS fltp ) AS ratio,

       div( num1, num2 ) AS div,

       mod( num1, num2 ) AS mod,

       @offset + abs( num1 - num2 ) AS sum

       FROM demo_expressions

       INTO CORRESPONDING FIELDS OF TABLE @results

       ORDER BY SUM DESCENDING.

 

 

SELECT id, CASE char1

             WHEN 'aaaaa' THEN ( char1 && char2 )

             WHEN 'xxxxx' THEN ( char2 && char1 )

             ELSE @else

           END AS text

       FROM demo_expressions

       INTO CORRESPONDING FIELDS OF TABLE @results.

 

More About

 

http://help.sap.com/abapdocu_740/en/index.htm?file=ABAPSQL_EXPR.htm

 

 

 

CDS Views

 

The new CDS (Core Data Services) enable you to define Views of the ABAP Dictionary with a DDL (Data Definition Language) in ADT. This DDL encompasses the DDL of SQL and enhances it with the possibility to define annotations and associations. CDS-Views in the Dictionary are not bound to a specific database platform. They provide another way of database independent code push down.You can acces CDS-Views with Open SQL.

 

Example

 

Definitiion of a simple view based on only one database table. Of course, you can join as you like ...

 

@AbapCatalog.sqlViewName: 'BPA_VW'
define view
business_partner as
  select from snwd_bpa
         { key bp_id, company_name, bp_role }

 

You can access the view in ABAP programs e.g. as follows:

 

SELECT * FROM business_partner INTO TABLE itab ...

 

More About

 

http://help.sap.com/abapdocu_740/en/index.htm?file=ABENCDS.htm

 

See also the dedicated blog http://scn.sap.com/community/abap/eclipse/blog/2014/02/04/new-data-modeling-features-in-abap-for-hana by Chris Swanepoel.

 

ABAP Managed Database Procedures

 

ABAP Managed Database Procedures (AMDP) are a class based framework for maintaining and calling stored procedures as so called AMD procedures from ABAP. An AMDP procedure is implemented in its native DB-language in an AMDP method of an AMDP class. Currently, the only database that suports AMDP is SAP's HANA database.


An AMDP class must implement a specific tag interface. Currently, there is only one namely IF_AMDP_MARKER_HDB. An AMDP class can be maintained in ADT only. An AMDP method looks from its declaration like a normal ABAP method and can be used like that. Only when implementing the method, you denote the database and the DB language. You also must denote the entities to be used. The following is an example for using the language SQLScript of the HANA database:

 

CLASS cl_demo_amdp DEFINITION PUBLIC.
  PUBLIC SECTION.
    INTERFACES if_amdp_marker_hdb .
    METHODS increase_price
      IMPORTING
        VALUE(clnt) TYPE sy-mandt
        VALUE(inc)  TYPE sflight-price .
ENDCLASS.

 

CLASS cl_demo_amdp IMPLEMENTATION.
  METHOD increase_price BY DATABASE PROCEDURE FOR HDB
                        LANGUAGE SQLSCRIPT
                        USING sflight.
    update sflight set price = price + :inc
                   where mandt = :clnt;
  ENDMETHOD.
ENDCLASS.

 

Please note that a simple example as the above one is only to show the syntax. Implementing functionality in an AMDP method means the usage of Native SQL. And for the usage of Native SQL the same holds as before: Stay open as long as possible! It makes absolutely no sense to push down statements to the database that you can also express in Open SQL, because those are pushed down by the Native SQL Interface in exactly the same way or even better!


You use AMDP as a convenient way of programming and handling Native SQL in ABAP only if you really gain something from it. And for that, simple examples are not too easy to find, especially since Open SQL is empowered by new features now (see above).

 

Example

 

A simple example of a database functionality that is not readily available in Open SQL is the predefined currency conversion of HANA. An AMDP method that uses this conversion might look as follows:

 

METHOD convert BY DATABASE PROCEDURE FOR HDB

                           LANGUAGE SQLSCRIPT

                           USING demo_prices.

  PRICES = select * from DEMO_PRICES;

  PRICES =

    CE_CONVERSION (

      :PRICES,

      [ family             = 'currency',

        method             = 'ERP',

        steps              = 'shift,convert,shift_back',

        target_unit        = :TO_CURRENCY,

        client             = :MANDT,

        source_unit_column = "CURRENCY",

        reference_date     = :DATE,

        output_unit_column = "CURRENCY",

        error_handling     = 'keep unconverted' ],

      [ amount AS amount ] );

   replace DEMO_PRICES select * from :PRICES;

ENDMETHOD.

 

An alternative implementation for databases other than the SAP HANA database must then be provided, e.g. as follows:

 

METHOD abap_convert.

  DATA prices TYPE STANDARD TABLE OF demo_prices.

  SELECT *

         FROM demo_prices

         INTO TABLE prices.

  LOOP AT prices ASSIGNING FIELD-SYMBOL(<price>).

    CALL FUNCTION 'CONVERT_TO_LOCAL_CURRENCY'

      EXPORTING

        client           = mandt

        date             = date

        foreign_amount   = <price>-amount

        foreign_currency = <price>-currency

        local_currency   = to_currency

      IMPORTING

        local_amount     = <price>-amount

      EXCEPTIONS

        OTHERS           = 4.

    IF sy-subrc <> 0.

      CONTINUE.

    ENDIF.

    <price>-currency = to_currency.

  ENDLOOP.

  MODIFY demo_prices FROM table prices.

ENDMETHOD.

 

ABAP code calling these methods could look as follows:

 

IF cl_db_sys=>is_in_memory_db = abap_true.

    NEW cl_demo_sqlscript_curr_conv(

      )->convert(

           EXPORTING

             to_currency      = to_upper( currency )

             mandt            = sy-mandt

             date             = sy-datlo ).

ELSE.

   NEW cl_demo_sqlscript_curr_conv(

     )->abap_convert(

          EXPORTING

            to_currency      = to_upper( currency )

            mandt            = sy-mandt

            date             = sy-datlo ).

ENDIF.

 

More About

 

http://help.sap.com/abapdocu_740/en/index.htm?file=ABENAMDP.htm

 

Further Information

 

For a complete overview of all ABAP Language News for Release 7.40, SP05 see

 

 

If you are especially interested in ABAP for HANA, also have a look at Jens Weiler's blog New ABAP for HANA features in SAP NW 7.4 SP5. The ABAP Keyword Documentation summarizes the respective ABAP language features under http://help.sap.com/abapdocu_740/en/index.htm?file=ABENABAP_HANA.htm.

 

Outlook

 

The next bundled release will be 7.40, SP08 with kernel release 742. Expect some nice features from that one too. I'm already very busy documenting them.


Dates and select options

$
0
0

Just a little note about a couple of common errors:

 

This is not rocket science ABAP programming.

 

But it does show how even the very basics can be mishandled. As ever, when developing, think about what is happening with the data in your variables. And always be prepared to challenge any spec of requirement, by leading the requester through the consequences.

 

What the user asks for and what the functional consultant requests are not always what they need!

ERROR 1: Using a select-option when you need a date range

 

If you want a start date and end date, it is a mistake to use a select option. It's a mistake often made by functional consultants as well. I've even challenged functional guys and they've insisted "no, I want a select option". When I've taken them through the requirement, they realise that they don't actually want a select option, they want a date range.

 

The reason it's a mistake is that the structure of a select option (an internal table with header line) is:

 

SIGN OPTION LOW HIGH

 

That means the user can enter all sorts of weird and wonderful combinations of selections, ranges and exclusions - and your code won't be able to handle it properly. What will you do if the user enters?

I BT 01.01.2014 31.01.2014

I BT 01.01.2013 31.01.2013

E EQ 02.01.2014

 

The user wants January 2013 and 2014, but not the second of January. But many date handling and validation routines, will fail with this selection. The user doesn't get what they expect.

 

ERROR 2: Mishandling an empty selection

 

Not infrequently, the specification will ask for special handling of an empty selection - something like "if the select is blank, then select nothing". This is also a mistake. An empty select option in standard SAP throughout the SAP system means "select everything". If you follow the functional consultants request, you've broken the expected functionality for the user.

 

So what's the solution?

 

Far better (and what most SAP programs do, where they really just want a start and end date) is to simply use:

 

PARAMETERS: start TYPE d, end TYPE d.

 

This solution satisfies the actual requirement 99% of the time, and makes your program behave in an expected way.

A "framework" for specific requirements in data transfer routines (and everywhere else?)

$
0
0

The premise


Hi guys and gals!

 

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

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

 


The idea


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


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

 

2014-02-07 11_37_52-Clipboard.png

 

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

 

The implementation!

 

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

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

 

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

 

Interface ZIF_VBAK_2_LIKP.png

With the following parameters:

Interface ZIF_VBAK_2_LIKP_2.png

 

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

 

Class ZCL_VBAK_2_LIKP_CORE Change.png

 

Class ZCL_VBAK_2_LIKP_4230 Display.png

 

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

 

2014-02-07 12_00_12-Clipboard.png

 

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

 

Class ZCL_VBAK_2_LIKP Change.png

 

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

 

PARAMETERS: p_vkorg TYPE vkorg.

BREAK-POINT.

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

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

 

Let's discuss!

 

So now the interesting part (for me). Let's discuss! What would you have done differently? While pasting I thought... it would probably be really cool to have the factory return a table of implementations and execute them one by one, pretty much like the BAdI framework. Would this be easy to implement? How? Hell, this could even be a living blog post, if I'm not mistaken blog posts can be edited by multiple people yes? If you're interested let me know

 

All my best,

Bruno

A small tip to get a list of changed objects at the given time period

$
0
0

sometimes we need to get a list of changed objects by the given user at the given time period, there is a small tip to quickly get this list with little effort.

 

approach1: query the table VRSD with user name and date as query parameter:

clipboard1.png


Each entry of list contains a corresponding transport request number ( in column KORRNUM )

clipboard2.png

However, this approach could not contain modifications on local objects.

 

The version number (5) and transport request number(AG3K074711) could also be reviewed in class builder menu Utilities

->Versions->Version management:

clipboard3.png

clipboard4.png

clipboard5.png

Approach2: query table TRDIR:

clipboard6.png

clipboard7.png

The list entry still contains the version number which could be found in version database.

clipboard8.png

clipboard9.png

Dynamic tables in ALV with RTTI

$
0
0

Dear all,

 

In this blog an example of dynamic tables in an ALV with RTTI is showed.

 

 

RTTI stands for Run Time Type Identification. It's used to get the definition of existing variables or existing types.

This can be used to create a dynamic structure and table type at runtime. These structure can easily change.

 

 

The next example have twoo screens:

  • One to give a name of an SAP table in parameter.
  • One to show all the values in the ALV with the given table from previous screen.

 

 

Below an example of a dynamic ALV with RTTI (code)

 

All the data that will be used.

TYPE-POOLS : abap, slis.
FIELD-SYMBOLS: <fs_table> TYPE STANDARD TABLE,                <fs_wa>,                <fs_field>.
DATA: zdyn_table    TYPE REF TO data,       zdyn_line     TYPE REF TO data,       zwa_fieldcat  TYPE lvc_s_fcat,       zit_fieldcat  TYPE lvc_t_fcat.
*ALV data declarations
DATA: zfieldcatalog  TYPE slis_t_fieldcat_alv WITH HEADER LINE,       zgd_tab_group  TYPE slis_t_sp_group_alv,       zgd_layout     TYPE slis_layout_alv,       zgd_repid      LIKE sy-repid.
PARAMETERS: zp_table(30) TYPE c .
DATA : zit_tabdescr TYPE abap_compdescr_tab,        zwa_tabdescr TYPE abap_compdescr.
DATA : zref_table_descr TYPE REF TO cl_abap_structdescr.

 

Return structure of the filled parameter. ZIT_TABDESCR[] will now have the structure and details (datatype, decimals, ... ) from the table.

zref_table_descr ?= cl_abap_typedescr=>describe_by_name( zp_table ).
zit_tabdescr[] = zref_table_descr->components[].

 

Set the structure of the table in the FIELDCAT, the structure of the ALV is based on this.

LOOP AT zit_tabdescr INTO zwa_tabdescr.   CLEAR zwa_fieldcat.   zwa_fieldcat-fieldname = zwa_tabdescr-name .   zwa_fieldcat-datatype  = zwa_tabdescr-type_kind.   zwa_fieldcat-inttype   = zwa_tabdescr-type_kind.   zwa_fieldcat-intlen    = zwa_tabdescr-length.   zwa_fieldcat-decimals  = zwa_tabdescr-decimals.   APPEND zwa_fieldcat TO zit_fieldcat.
ENDLOOP.


Create dynamic internal table and assign to Field Symbol

CALL METHOD cl_alv_table_create=>create_dynamic_table   EXPORTING     it_fieldcatalog = zit_fieldcat   IMPORTING     ep_table        = zdyn_table.
ASSIGN zdyn_table->* TO <fs_table>.

 

Create dynamic work area and assign to Field Symbol

CREATE DATA zdyn_line LIKE LINE OF <fs_table>.
ASSIGN zdyn_line->* TO <fs_wa>.

 

When the dynamic table is setted up, this one can be filled with an SELECT statement. It gets all the values from the filed parameter.

SELECT * INTO CORRESPONDING FIELDS OF TABLE  <fs_table>
FROM (zp_table).

 

An ALV Function module to build field catalog from the dynamic created table.

DATA: zit_fcat  TYPE slis_t_fieldcat_alv.
CALL FUNCTION 'REUSE_ALV_FIELDCATALOG_MERGE'   EXPORTING     i_structure_name       = zp_table   CHANGING     ct_fieldcat            = zit_fcat   EXCEPTIONS     inconsistent_interface = 1     program_error          = 2     OTHERS                 = 3.
IF sy-subrc <> 0.   MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno           WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.

 

Fieldcatalog is moved to a new fieldcatalog

zfieldcatalog[] =  zit_fcat[].

 

To optimize every field length:

zgd_layout-colwidth_optimize = 'X'.

 

The ALV will be showed with the next function module. Layout, fieldcatalog and repid are given in EXPORTING, also the table with al the values.

zgd_repid = sy-repid.
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'   EXPORTING     i_callback_program = zgd_repid     is_layout          = zgd_layout     it_fieldcat        = zfieldcatalog[]     i_save             = 'X'   TABLES     t_outtab           = <fs_table>   EXCEPTIONS     program_error      = 1
OTHERS             = 2.

 

 

Kind regards,

 

Pieter Lemaire

Use report RSDEPEND to analyze ABAP load dependencies

$
0
0


In SAP note 1230076 "Generation of ABAP loads: Tips for the analysis", a tool report RSDEPEND is introduced.

 

It is explained in the note "An ABAP program generally depends on many other repository objects. If an object like this changes (for example, an include or a DDIC type), the load of all dependent programs must be invalidated. The load of these programs is then regenerated with the next use, and valid loads are generated again."

 

In order to demonstrate the ABAP load invalidation logic, I create a very simple database table ZCRMM_CCTV_CHAL and write a simple report ZTESTLOAD to fetch all its data:

 

 

data: lt_table type STANDARD TABLE OF ZCRMM_CCTV_CHAL.

select * INTO TABLE lt_table FROM ZCRMM_CCTV_CHAL.


1. Create and activate the test report for the first time

Execute report RSDEPEND with ZTESTLOAD as program name = ZTESTLOAD.

 

it returns the result as below. In the first part we see the timestamp of ABAP load and ABAP source are both initial one when I activate the report.

 

In the second part we see our simple report has many dependencies on system includes like <REPINI> and <SYSINI>.

Those system includes are automatically inserted into the test program I have created, it is not necessary for application developers to manually include them, or else there would be compilation errors:

error.png

 

 

In the third part "Dependencies of Dictionary Types", we found the depended database table which was initially created on 10.21 in year 2013.

clipboard1.png

2. only change the depended database table description

clipboard2.png

Re-run RSDEPEND report:

 

  • The timestamp of ABAP load and ABAP source of the test report ZTESTLOAD remain unchanged;
  • The timestamp "last changed" of database table ZCRMM_CCTV_CHAL changed to the time when I change the table description;
  • The ABAP timestamp and Screen timestamp of database table ZCRMM_CCTV_CHAL remain unchanged;

clipboard3.png

3. Add a new column to database table

clipboard4.png

The execution result of RSDEPEND report:

 

  • The timestamp of ABAP load and ABAP source of the test report ZTESTLOAD remain unchanged;
  • The timestamp "last changed" of database table ZCRMM_CCTV_CHAL changed to the time when I change the table description;
  • The ABAP timestamp and Screen timestamp of database table ZCRMM_CCTV_CHAL also changed to the time when I change the table description;

 

clipboard5.png

4. Execute the test report ZTESTLOAD

The execution result of RSDEPEND report:

 

 

 

  • The timestamp of ABAP load and ABAP source of the test report ZTESTLOAD remain unchanged;
  • The timestamp "last changed" of database table ZCRMM_CCTV_CHAL changed to the time when I change the table description;
  • The ABAP timestamp and Screen timestamp of database table ZCRMM_CCTV_CHAL also changed to the time when I change the table description;

clipboard6.png

 

Viewing all 943 articles
Browse latest View live


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