Quantcast
Channel: Tweaks
Viewing all 66 articles
Browse latest View live

SAP ABAP Checkpoint Group – Chase the Mysterious SAP Issues with a Smile

$
0
0

T-Code SAAB for SAP ABAP Checkpoint Group

If changes are made to the code, then there is no guarantee that previous assumptions are satisfied. SAP ABAP checkpoints can be used for making sure that program correctness is maintained. Under checkpoints, we consider Assertions, Breakpoint, and Log points.

Assertions can be used for improving the quality of software. Breakpoint and Log point are used for investigating program behavior in case of problems. They help in understanding and maintaining the code.

The transaction SAAP has been there for quite some time but it is a pity not many ABAPers are aware of it. Therefore we thought our SAPYard family members should know about this useful tool. Better late than never. It is such a handy tool, which might even be useful in the real production environment.

Checkpoint Group
The activation state of all checkpoints which can be activated is controlled by the checkpoint group.

Assertion
Let us consider a scenario where money is transferred from one account to another. Here, the sum of both balances must be equal before and after transfer. Simple Finance. Right? 🙂 With the help of assertion, we can check this condition. It is a statement which we can put in the program describing a specific condition.

syntax 1 – ASSERT logical expression.

During program execution, if expression fails, execution can be stopped. Program execution can be stopped by raising ASSERTION_FAILED.

syntax 2 – ASSERT ID checkpoint group CONDITION logical expression.

If ID statement is used then assertion activation and its behavior is controlled from outside the program by checkpoint group otherwise it is always active.

How to create Checkpoint group?

Checkpoint group can be created using SAAB transaction.

SAAB

Enter Name and click on create. Save it.

Please note – By default Assertion is Inactive as shown below in the Assertions frame.

How to create checkpoint group?

Also Read – Lazy and Smart ABAPers

Let us write a very basic program and execute it in SE38.

REPORT ZSAPYardCheckPoint.

DATA : lv1 TYPE c VALUE 'A'.
DATA : lv2 TYPE c VALUE 'B'.

INITIALIZATION.

  LOG-POINT ID zcheck_pt FIELDS: lv1,lv2.

START-OF-SELECTION .

  lv1 = 'X'.
  lv2 = 'Y'.

  LOG-POINT ID zcheck_pt FIELDS: lv1,lv2.

  WRITE 'Run.. Done'.

 ASSERT ID zcheck_pt CONDITION lv1 EQ lv2.

 BREAK-POINT ID zcheck_pt.

Execute the program. It would run successfully and write the output “Run.. Done”.

Case 1 – Abort

Now, go to transaction SAAB. Select radio button Abort in Assertions frame and save the Checkpoint Group.

How to activate Assert

Now, execute the program again. You will get a dump describing the position where Assertion is violated.

Assertion is violated

Did you check? Playing Sherlock Holmes to detect CONVT_CODEPAGE runtime error mystery

You can no doubt see the dump in the regular t-code ST22. But our aim in this article is to see the Log in SAAB T-code where we created the Checkpoint.

Case 2 – Log

In order to start logging, we need let the SAAB T-Code know about our intention.  We need to choose the Log radio button (as shown below) in the Assertions (Foreground) section.

Log radio button

You can see the log details with Variable values, which would be very useful in cases where we can’t replicate the scenarios in Quality or Development box – to chase the mysterious missing values.

Assert log

PS: If you do not see the above log, choose the Log radio button in Assertions frame and re-run your program.

Case 3 – Break

When you select Break option you will get following pop up which is just an information.

What does the above information mean?

Ans: In the normal case, the program is interrupted and the debugger is started. In case of background processing, there are two choices. It will behave like Log mode or Abort mode depending on what is selected in the above pop up. By default it is Log, so in Background processing mode, the program would not go into debug mode, but checkpoint group would log the issues.

Just like Assertions; Breakpoints and Logpoints can be activated by selecting Break and Log respectively.

break and log

BREAK-POINT:
Break Point can be activated by writing following code in your program.

BREAK-POINT ID <Checkpoingroup>.

breakpoint activation

Active breakpoint behaves same as an Always Active breakpoint. In case of background processing, activatable breakpoints are simply ignored. Run the Program in the foreground, you can see the program stop by at line 30.

LOG-POINTS:
Log Point can be made active by writing following code in your program.

LOG-POINT ID <Checkpoingroup>

Logs can be used to identify or analyze the system behavior. Variable values can be logged so that the developers can analyze those values.

You can add in your program statement LOG-POINT ID ZCHEECK_PT. You can also use the following syntax.

Run the program and go to transaction SAAB. Go to tab ‘LOG’. You will see following details which will help you to analyze program’s behavior.

Above statement log the value of field ‘log’. We can log up to 32 fields.

You can use SUBKEY addition to prevent the production of a huge amount of data in the log. All log occurrences will produce one record for the same SUBKEY. Only last occurrence can be seen but the counter will be incremented.

LOG_POINT ID ZTRY SUBKEY ‘TEST’ FIELDS log.

Also Read – DELETING rows of the internal table within the LOOP. Is it a Taboo? A big NO NO?

Checkpoint group activation can be done with three levels

  1. Personal Activation – Checkpoint group will be active for current user only.

2. User Level activation – Checkpoint group will be active for all defined users.

User Level Activation of Log

3. Server level Activation – The Same way we can define servers for which it will be active.

This was my humble effort to bring your notice to this simple yet very powerful tool which every ABAPer should make use of. I know, this transaction SAAB should have been introduced years ago. But every day you find something new in SAP. Please start using it and let me know if you face any issue. I would try to answer your queries.

If there any other useful tool which you use in your project?

Please share with us if you have some smart way to tackle production issues or identify dumps. If you have some custom tools which can be shared, do send it to us. We would be happy to share it in our portal.

Now, we want to hear from you. Please leave your feedback and comments below.

We have a very active Telegram (App) SAP Technical Group with more than 1440+ SAP Technical Practitioners from 6 Continents of the SAP World. Please join it using below link.
Telegram SAP Technical Discuss Group. You need to install the Telegram App first on your mobile device. Once you have it on your mobile, you can join the group and also access it from the Web on your computer and laptop.

Check Step by Step Tutorials on SAP HANA-ABAP


Calculator in SAP using New ABAP Syntax

$
0
0

SAP HANA is Hot. Fiori/UI5 is Cool. OData is Fascinating. But in all these fun, basic ABAP is still thriving. This article is an example of the basic ABCs of ABAP which we need till ABAP does not get extinct (which will never happen) in the future SAP World. 🙂 Darwin’s law of Evolution holds good to ABAPers as well. ABAPers need to evolve and get new tricks of the game in their kitty, if they want to stay ahead in the competition. In this article, I have tried to show case some of the New Syntaxes of ABAP, which makes programming easier and reduces the code footprints.

The context behind this article:

In our SAPYard Technical Telegram Group, where we have more than 2470 active consultants from 6 Continents, one member @imkinnu shared the below image/gif and asked the Question.

Also Check – Sudoku Solver and Generator Tool Using Vanilla SAP ABAP

Question: The result should be printed in Result box instead of the Input text. For example, for Input ‘2+3’ I need the Result ‘5’ to be printed in result box. What i have done is, assigned the Input field ‘IN’ to Result field ‘RES’.

Answer from @Anask (within few seconds. That the beauty of this group):
It treats the variable as string and just copies content of one field to another. You need to split at SIGN and get data into 2 separate fields and then do the operator action. Just use Split and then do the Math operation. Use CASE statement and split based on the sign it contains.

Sample Code:

IF input CS "+".
lv_operator = "+".
ELSEIF input CS "-".
lv_operator = "+".
ELSEIF input CS "*".
lv_operator = "*".
ELSEIF input CS "/".
lv_operator = "/".
ENDIF.

SPLIT lv_input AT lv_operator
INTO lv_operand1
lv_operand2.

I am members of many WhatsApp and Telegram and other groups. But this SAP Technical Group is the most active one where we have tons of discussions every hour. Most Questions get Answered or at least get some ideas for the solution. If you have not joined yet, give it a try. It is safe and secure. And no one can know your mobile number, not even the Admins. You need to have Telegram App installed in your device before you join the group using the below link.

Join SAPYard’s SAP Technical Group.

I thought of helping @imkinnu with my own program. I quickly wrote a simple OOPs Program with New ABAP Syntaxes which I try to write in all my new developments.

Well, I forgot to introduce myself. My name is Stephan Köster. I am one of the Admins of the SAPYard SAP Technical Group. I am an SAP ABAP Developer. I am the founder of Köster Consulting. Please check my website for more information.

Enough of me. Now, lets come back to the topic of the day. If you are a novice of ABAP7.4, some of the points which you need to note in the below SAP Calculator Program are:

1. Inline Data Declaration

DATA(lv_off) = sy-index - 1.
DATA(lv_add) = abap_true.
READ TABLE gt_string_tab ASSIGNING FIELD-SYMBOL(<lv_op>) WITH TABLE KEY table_line = '/'.

2. RAISE exceptions

RAISE missing_number_at_beginning.
RAISE two_operator_not_allowed.
RAISE division_by_zero.
RAISE missing_number_at_end.
RAISE unknown_error.

(Nothing new. Showing for beginners in ABAP)

3. Conversion Function

DATA(lv_result) = CONV labst( <lv_first> / <lv_second> ).
rv_result = CONV #( gt_string_tab[ 1 ] ).
DATA(gv_fieldname) = |RB_CALC{ CONV numc1( sy-index ) }|.
cv_editable = |PA_CALC{ CONV numc1( sy-index ) }|.

4. New Syntax

INSERT |{ lv_result }| INTO gt_string_tab INDEX lv_tabix - 1.

5. Inline Data Declaration in Class Methods

* Call the Class Method to do the calculation

zcl_stkoes_calc=>do_calculation(
EXPORTING
iv_formula = <gv_calc>
RECEIVING
rv_result = DATA(gv_result)
EXCEPTIONS
division_by_zero = 1 " Division By Zero
missing_number_at_beginning = 2 " Missing Number at the beginning
missing_number_at_end = 3 " Missing Number at the end
two_operator_not_allowed = 4 " Two Operator are not allowed
unknown_error = 5 " Unknown Error
OTHERS = 6 ).

Did you check gv_result is declared during the call of the method do_calculation.

Note: We canNOT do similar Inline Data Declaration while calling a Function Module. Only available in Class Method.

6. MESSAGE with SWITCH

MESSAGE |{ SWITCH #( sy-subrc
WHEN 1 THEN |Division by zero|
WHEN 2 THEN |Missing number at the beginning|
WHEN 3 THEN |Missing number at the end|
WHEN 4 THEN |Two operator is not allowed|
WHEN 5 THEN |Unknown Error|
ELSE |Other Error| ) }| TYPE 'S' DISPLAY LIKE 'E'.

Also Check: T-Guard: Transport Dependency Validation Tool

Let me show you, what the Calculator can do. It does the basic +, -, X, / mathematic operations. But the logic and concept which I have put in the class can be extrapolated to many real use case actual project requirements.

I have knowingly used the Macros to make you understand how Macros can still be used. If you feel, macros are not needed, you can do the direct Radio Buttons in the Selection Screen without the Macros.

Sample Input String for the Calculator and their corresponding Results
SAP Calculator in ABAP

Sample Error Handling

Also Read: GPS like tool in SAP using Google Map API

Here is the complete Code which you can try. You can also download the program text file at the bottom of this article.

*&---------------------------------------------------------------------*
*& Report ZSTKOES_CALC
*&---------------------------------------------------------------------*
*& This is a Utilty Program which acts like a Simple Calculator.
*& OOPs Concept along with New ABAP7.4+ Syntaxes are used
*& Feel Free to refer and use it as required
*&---------------------------------------------------------------------*
REPORT zstkoes_calc.

CLASS zcl_stkoes_calc DEFINITION.

PUBLIC SECTION.
CONSTANTS:
BEGIN OF gcs_operators,
div TYPE char1 VALUE '/' ##NO_TEXT,
mod TYPE char3 VALUE 'MOD' ##NO_TEXT,
mul TYPE char1 VALUE '*' ##NO_TEXT,
sub TYPE char1 VALUE '-' ##NO_TEXT,
add TYPE char1 VALUE '+' ##NO_TEXT,
END OF gcs_operators.

CLASS-METHODS:
do_calculation
IMPORTING
VALUE(iv_formula) TYPE string
RETURNING
VALUE(rv_result) TYPE labst
EXCEPTIONS
division_by_zero
missing_number_at_beginning
missing_number_at_end
two_operator_not_allowed
unknown_error.

PRIVATE SECTION.
TYPES:
gtty_string TYPE TABLE OF string.
CLASS-METHODS:
convert_formula_to_string_tab
IMPORTING
VALUE(iv_formula) TYPE string
EXPORTING
VALUE(et_string_tab) TYPE gtty_string
EXCEPTIONS
missing_number_at_beginning
missing_number_at_end
two_operator_not_allowed,

calculate
IMPORTING
VALUE(it_string_tab) TYPE gtty_string
RETURNING
VALUE(rv_result) TYPE labst
EXCEPTIONS
division_by_zero
unknown_error.
ENDCLASS.

CLASS zcl_stkoes_calc IMPLEMENTATION.
METHOD do_calculation.

convert_formula_to_string_tab(
EXPORTING
iv_formula = iv_formula
IMPORTING
et_string_tab = DATA(lt_string_tab)
EXCEPTIONS
missing_number_at_beginning = 1
missing_number_at_end = 2
two_operator_not_allowed = 3 ).

IF sy-subrc EQ 0.
calculate(
EXPORTING
it_string_tab = lt_string_tab
RECEIVING
rv_result = rv_result
EXCEPTIONS
division_by_zero = 4
unknown_error = 5 ).
ENDIF.

IF sy-subrc NE 0.
CASE sy-subrc.
WHEN 1.
RAISE missing_number_at_beginning.
WHEN 2.
RAISE missing_number_at_end.
WHEN 3.
RAISE two_operator_not_allowed.
WHEN 4.
RAISE division_by_zero.
WHEN 5.
RAISE unknown_error.
ENDCASE.
ENDIF.

ENDMETHOD.

METHOD convert_formula_to_string_tab.
FIELD-SYMBOLS:
<lv_value> TYPE string.

CONDENSE iv_formula NO-GAPS.
DATA(lv_off) = 0.
DO.
IF iv_formula+lv_off(1) CN '1234567890'.
DO.
ASSIGN COMPONENT sy-index OF STRUCTURE gcs_operators TO FIELD-SYMBOL(<lv_operator>).
IF sy-subrc NE 0.
EXIT.
ENDIF.
DATA(lv_length) = strlen( <lv_operator> ).
IF iv_formula+lv_off(lv_length) EQ <lv_operator>.
IF lv_off EQ 0.
RAISE missing_number_at_beginning.
ELSEIF <lv_value> IS NOT ASSIGNED.
RAISE two_operator_not_allowed.
ENDIF.
UNASSIGN <lv_value>.
APPEND iv_formula+lv_off(lv_length) TO et_string_tab.
ADD lv_length TO lv_off.
EXIT.
ENDIF.
ENDDO.
ELSE.
IF <lv_value> IS NOT ASSIGNED.
APPEND INITIAL LINE TO et_string_tab ASSIGNING <lv_value>.
<lv_value> = iv_formula+lv_off(1).
ELSE.
<lv_value> = |{ <lv_value> }{ iv_formula+lv_off(1) }|.
ENDIF.
ADD 1 TO lv_off.
ENDIF.
IF lv_off EQ strlen( iv_formula ).
EXIT.
ENDIF.
ENDDO.

IF <lv_value> IS NOT ASSIGNED.
RAISE missing_number_at_end.
ENDIF.
ENDMETHOD.

METHOD calculate.
DO.
ASSIGN COMPONENT sy-index OF STRUCTURE gcs_operators TO FIELD-SYMBOL(<lv_operator>).
IF sy-subrc NE 0.
EXIT.
ENDIF.

DO.
ASSIGN it_string_tab[ table_line = <lv_operator> ] TO FIELD-SYMBOL(<lv_op>).
IF sy-subrc NE 0.
EXIT.
ENDIF.
DATA(lv_from) = sy-tabix - 1.
DATA(lv_to) = sy-tabix + 1.
READ TABLE it_string_tab ASSIGNING FIELD-SYMBOL(<lv_first>) INDEX lv_from.
READ TABLE it_string_tab ASSIGNING FIELD-SYMBOL(<lv_second>) INDEX lv_to.
IF <lv_first> IS ASSIGNED AND <lv_second> IS ASSIGNED.
TRY.
CASE <lv_operator>.
WHEN '/'.
DATA(lv_result) = CONV labst( <lv_first> / <lv_second> ).
WHEN 'MOD'.
lv_result = <lv_first> MOD <lv_second>.
WHEN '*'.
lv_result = <lv_first> * <lv_second>.
WHEN '-'.
lv_result = <lv_first> - <lv_second>.
WHEN '+'.
lv_result = <lv_first> + <lv_second>.
ENDCASE.
CATCH cx_sy_zerodivide INTO DATA(lo_error).
RAISE division_by_zero.
ENDTRY.
DELETE it_string_tab FROM lv_from TO lv_to.
INSERT |{ lv_result }| INTO it_string_tab INDEX lv_from.
ENDIF.
ENDDO.
ENDDO.

IF lines( it_string_tab ) EQ 1.
rv_result = it_string_tab[ 1 ].
ELSE.
RAISE unknown_error.
ENDIF.
ENDMETHOD.
ENDCLASS.

**--------------------------------------------------------------------*
** Start of Program
**--------------------------------------------------------------------*
* Macro to set one radiobutton as default (can be used only once)
DEFINE calc_default.
SELECTION-SCREEN:
BEGIN OF LINE.
PARAMETERS:
rb_calc&1 RADIOBUTTON GROUP cal DEFAULT 'X' USER-COMMAND calc&1.
SELECTION-SCREEN:
COMMENT 3(14) co_calc&1.
PARAMETERS:
pa_calc&1 TYPE string DEFAULT &2.
SELECTION-SCREEN:
END OF LINE.
END-OF-DEFINITION.

* Macro to create radiobutton without deafult
DEFINE calc.
SELECTION-SCREEN:
BEGIN OF LINE.
PARAMETERS:
rb_calc&1 RADIOBUTTON GROUP cal.
SELECTION-SCREEN:
COMMENT 3(14) co_calc&1.
PARAMETERS:
pa_calc&1 TYPE string DEFAULT &2.
SELECTION-SCREEN:
END OF LINE.
END-OF-DEFINITION.

SELECTION-SCREEN BEGIN OF BLOCK b01 WITH FRAME TITLE gt_b01.

* Creating all paramater on SELECTION SCREEN using macro
calc_default 1 '10 + 300 / 100 * 10 - 50'.
calc 2 '+10 + 300 / 100 * 10 - 50'.
calc 3 '10 + 300 / 100 * 10 - 50+'.
calc 4 '10 + 300 / 100 * * 10 - 50'.
calc 5 '10 + 300 / 0 * 10 - 50'.
SELECTION-SCREEN END OF BLOCK b01.

INITIALIZATION.
* Filling all comments and title
co_calc1 = '1. Calculation'.
co_calc2 = '2. Calculation'.
co_calc3 = '3. Calculation'.
co_calc4 = '4. Calculation'.
co_calc5 = '5. Calculation'.
gt_b01 = 'Calculations'.

AT SELECTION-SCREEN OUTPUT.
DATA:
gv_editable TYPE rsscr_name.

* Check which radiobutton is selected
PERFORM get_field_selected_radiobutton CHANGING gv_editable.

* Leave only selected line editable
LOOP AT SCREEN.
IF screen-name(7) EQ 'PA_CALC' AND screen-name NE gv_editable.
screen-input = 0.
MODIFY SCREEN.
ENDIF.
ENDLOOP.

START-OF-SELECTION.
DATA:
gv_editable TYPE rsscr_name.

* Check which radiobutton is selected
PERFORM get_field_selected_radiobutton CHANGING gv_editable.

* Get the Formular of selected radiobutton
ASSIGN (gv_editable) TO FIELD-SYMBOL(<gv_calc>).
IF <gv_calc> IS ASSIGNED.

* Do the calculation
zcl_stkoes_calc=>do_calculation(
EXPORTING
iv_formula = <gv_calc>
RECEIVING
rv_result = DATA(gv_result)
EXCEPTIONS
division_by_zero = 1 " Division By Zero
missing_number_at_beginning = 2 " Missing Number at the beginning
missing_number_at_end = 3 " Missing Number at the end
two_operator_not_allowed = 4 " Two Operator are not allowed
unknown_error = 5 " Unknown Error
OTHERS = 6 ).

* Handle Errors
IF sy-subrc NE 0.
MESSAGE |{ SWITCH #( sy-subrc
WHEN 1 THEN |Division by zero|
WHEN 2 THEN |Missing number at the beginning|
WHEN 3 THEN |Missing number at the end|
WHEN 4 THEN |Two operator is not allowed|
WHEN 5 THEN |Unknown Error|
ELSE |Other Error| ) }| TYPE 'S' DISPLAY LIKE 'E'.
RETURN.
ENDIF.
ENDIF.

END-OF-SELECTION.

WRITE: gv_result.
*&---------------------------------------------------------------------*
*& Form GET_FIELD_SELECTED_RADIOBUTTON
*&---------------------------------------------------------------------*
* <--CV_EDITABLE PARAMETER of selected radiobutton
*----------------------------------------------------------------------*
FORM get_field_selected_radiobutton CHANGING cv_editable TYPE rsscr_name.
DO.
DATA(lv_fieldname) = |RB_CALC{ CONV numc1( sy-index ) }|.
ASSIGN (lv_fieldname) TO FIELD-SYMBOL(<lv_fieldvalue>).
IF sy-subrc NE 0.
EXIT.
ELSEIF <lv_fieldvalue> EQ abap_true.
cv_editable = |PA_CALC{ CONV numc1( sy-index ) }|.
EXIT.
ENDIF.
ENDDO.

ENDFORM.

Download above program in text file. Calculator Version 1

If you do not want to use Macros for the Radio Buttons, you can download another version of the same program with simpler Selection screen. Calculator Version 2

Output of Version 2 of the SAP ABAP Calculator looks like below.

ABAP Calculator

I hope, this article would motivate you to use the New ABAP Syntax in all your current and future developments. We need to embrace the change and accept it in our daily projects. Change is Progress. 🙂

Very recently, we have started Free Video Courses. Please check our End to End Video Training on SAP OData and Netweaver Gateway.

OData Video Training

Also check our Free Video Course on SAP HANA, HANA DB and ADT.

Free SAP HANA End to End Training

Please Subscribe to our YouTube Channel for more Educative SAP Technical Videos.

SO10 Enhancement to Add Texts to the Transport Request Automatically

$
0
0

Putting Text in SO10 and saving it to a transport and moving it to Quality and Production is a common requirement. It is a two step process. First save the text and then move the text to transport using program RSTXTRAN. But many times, developer or the person saving the text, forgets to save the text in the transport as it does not prompt for a transport while saving.

If you want to know the regular two step process to move the SO10 text to a transport, check this sap blog.

In our current project, we had a human error and we did not move the text to production. And Security Team did not allow us to save text directly in the production system.

Our manager asked us to find a way so that this issue does not repeat. After some brain storming session, we along with other architects, came up with a solution to prompt users to save the text in transport while saving the text in t-code SO10. This way, we do not forget.

The solution was to put an Implicit Enhancement in the Include program  LSTXXFTE (Form TE_SAVE_TEXT). We programmed it to prompt for the transport number, the move the Save button is hit.

Note : There might be many opponents of Implicit Enhancement. If you have a better way to meet the same requirement, please do let us know. We would be happy to apply it.

A short Youtube link which goes through our enhanced functionality and design.

Also check our Free Video Courses on SAP HANA, OData and New Syntaxes of SAP ABAP 7.40.

SAP Video Courses for Free

Implicit Enhancement in Include Program LSTXXFTE. At the end of the Sub routine TE_SAVE_TEXT.

Source Code Snippet.

DATA : lo_utlities TYPE REF TO zcl_utilities,
ls_input TYPE zsavetextinput,
lo_subrc TYPE REF TO sy-subrc.

FIELD-SYMBOLS: <lfs_any> TYPE any.

** Get instance
lo_utlities = zcl_utilities=>factory( ).
IF lo_utlities IS BOUND.
** FIll input
ls_input-tdname = tename.
ls_input-tdid = teid.
ls_input-tdspras = tespras.
** Call save method
lo_subrc =  lo_utlities->save_standradtext( is_input = ls_input ).
ASSIGN lo_subrc->* TO <lfs_any>.
IF <lfs_any> = 0.
** Success
ENDIF.
ENDIF.
ENDIF.

Also Read SAP Calculator using New Syntax

ZCL_UTILITY class is created and all logic are handled in it.

class ZCL_UTILITIES definition
public
final
create private .

public section.
*"* public components of class ZCL_UTILITIES
*"* do not include other source files here!!!

class-methods FACTORY
returning
value(RT_REF) type ref to ZCL_UTILITIES .
methods SAVE_STANDRADTEXT
importing
!IS_INPUT type ZSAVETEXTINPUT
returning
value(LO_OUTPUT) type ref to SY-SUBRC .
protected section.
*"* protected components of class ZCL_UTILITIES
*"* do not include other source files here!!!
private section.
*"* private components of class ZCL_UTILITIES
*"* do not include other source files here!!!
ENDCLASS.

CLASS ZCL_UTILITIES IMPLEMENTATION.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_UTILITIES=>FACTORY
* +-------------------------------------------------------------------------------------------------+
* | [<-()] RT_REF                         TYPE REF TO ZCL_UTILITIES
* +--------------------------------------------------------------------------------------</SIGNATURE>
method FACTORY.

CREATE OBJECT rt_Ref.
if rt_ref is bound.
endif.
endmethod.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_UTILITIES->SAVE_STANDRADTEXT
* +-------------------------------------------------------------------------------------------------+
* | [--->] IS_INPUT                       TYPE        ZSAVETEXTINPUT
* | [<-()] LO_OUTPUT                      TYPE REF TO SY-SUBRC
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD save_standradtext.

DATA:
lv_tr_order_zz     TYPE e070-trkorr,
lv_tr_task_zz      TYPE e070-trkorr,
lv_textkey_zz(110),
lv_len_zz          TYPE i.

DATA:
lt_e071_zz  TYPE TABLE OF e071,
lt_e071k_zz TYPE TABLE OF e071k,

ls_e071_zz  TYPE e071,
ls_e071k_zz TYPE e071k.

FIELD-SYMBOLS:
<lv_zz> TYPE any.

IF sy-subrc = 0.

CALL FUNCTION 'TR_ORDER_CHOICE_CORRECTION'
EXPORTING
iv_category            = 'CUST'
iv_cli_dep             = 'X'
IMPORTING
ev_order               = lv_tr_order_zz
ev_task                = lv_tr_task_zz
EXCEPTIONS
no_correction_selected = 1.

IF sy-subrc = 0.

lv_textkey_zz = '%,$,%,%'.

REPLACE '%' WITH 'TEXT'   INTO lv_textkey_zz.
REPLACE '%' WITH is_input-tdid     INTO lv_textkey_zz.
REPLACE '%' WITH is_input-tdspras  INTO lv_textkey_zz.

CONDENSE lv_textkey_zz NO-GAPS.

lv_len_zz = strlen( is_input-tdname ).
ASSIGN is_input-tdname(lv_len_zz) TO <lv_zz>.
REPLACE '$' WITH <lv_zz> INTO lv_textkey_zz.

ls_e071_zz-obj_name = lv_textkey_zz.
ls_e071_zz-trkorr = lv_tr_task_zz.
ls_e071_zz-as4pos = 0.
ls_e071_zz-pgmid  = 'R3TR'.
ls_e071_zz-object = 'TEXT'.
*     ls_e071_zz-objfunc = objfunc.

APPEND ls_e071_zz TO lt_e071_zz.

CALL FUNCTION 'TR_APPEND_TO_COMM_OBJS_KEYS'
EXPORTING
wi_trkorr     = lv_tr_task_zz
TABLES
wt_e071       = lt_e071_zz
wt_e071k      = lt_e071k_zz
EXCEPTIONS
error_message = 99.
IF sy-subrc = 0.
GET REFERENCE OF sy-subrc INTO lo_output.
ENDIF.

ENDIF.
ENDIF.
ENDMETHOD.
ENDCLASS.

Hope you liked this trick. If you have any tips or tweaks, please share them with us at mail@sapyard.com and we would be happy to publish them here.

We also have a 2550+ active SAP Consultants from 6 continents in our Telegram Group. Please join and be part of the largest knowledge sharing group in SAP Technical – SAPYard Technical Telegram Group.

Also check other Code Snippets.

4 Smart Steps to Avoid Junk Characters using SAP Language Translation Approach

$
0
0

SAP Language Translation has been there since the birth of SAP. If you are in a project with Global Roll Outs, you must have faced the issue of Language and Translation. In the mid 2000 era, I was working for a USA client who were rolling out for South America and Asia. The USA release was perfect but we had to put major effort for Language and Currency conversions for other geographies.

Application developers (read ABAPer) often get junk character issue during Form Translations in roll out engagements. Though Language Translation activity is one of the basic requirements in SAP Forms (SmartScript, SmartForm or Adobe), at times if can consume high efforts to troubleshoot junk character issues, and stress the developers and the team to great extend, if not approached diligently. 🙂

Also Read : Free Video Tutorials on HANA, OData and ABAP 7.40

We have followed the simple below strategy for smooth Translation Activity for localize requirement. You might use it as a guide for  your project needs.

Pre-requisites for any Language Translation Activity

If you are in a team who are performing the language translation activity, do not neglect the below two points:

1. Configure and Use Correct Font for Printing

For instance, we need to use JPMINCHO for language Japanese. Similarly for Chinese language, we should use CNSONG or CNHEI.

2. Configure and Use Correct Device Type and Printer

Having said that, the million dollar question is, how do we really identify the correct font and device type that should be used?

How? How? How?

Do not worry. We are here to help you. Adopt the below simple procedures and you would never see those unwanted characters in your output forms.

Hands on Exercise

For our understanding we would pick Chinese Language for Translation. We know that for Simplified Chinese, ‘ZH’ is the SAP language code.

If you want to know the Language Keys or SAP Language Code, then go to table T002.

Translation in SAP

Coming back to the exercise, we would see how we can Translate a simple Purchase Order Texts from English to Chinese.

Below steps would help us identify the correct Font and Device Type which in turn would help us address the Language Translation requirement.

Step 1: Prepare Language Translation Template

  • A language translation template should be prepared to collate the localize language (Chinese (ZH) for our example) from the business/end/super users.

Step 2: Identify the Correct Font

Identifying the correct font that should be used for the Purchase Order Form is our next step.

  • Go to t-code SE71, give the SAPScript name, for PO case it is MEDRUCK and Language as ‘ZH’. Select the Paragraph Formats and hit display.

sapscript for po

  • Click the Font to get the Chinese Font name. Check the font is CNHEI. We need to leverage the same for our business case.

chinese font in sap

For some of us, it might look like below.

Also Read : Step by Step Tutorials on SAP Adobe Forms

Step 3 : Identify the Correct Device Type

Now we need to analyse and figure out the correct device type which would support the Chinese font CNHEI identified above.

  • The SAPscript Printer Fonts table TFO03 comes to our rescue. Provide the font identified (CNHEI) in above step and get the correct Device Type. It has one to many relationship. Based on the fonts, character per inch, bold, italics etc choose the most appropriate one for your landscape.

Step 4: Identify the correct Printer for the Device Type

  • The Spool Printer declaration table TSP03 has the information. Provide the device type CNHPLJ4 and it would provide you the correct Printer.

Above is one of the Printer which supports Chinese Language.

That’s it. Follow the above 4 steps and have a smooth Translation and Printing activity. This is my first article at SAPYard. Please do provide your genuine feedback. It would help me for my next article.

Download Telegram App in your mobile and join the most active SAP Technical Discussion Group where we have dedicated members (2580+) from 6 continents.
Telegram SAP Technical Discuss Group. 

Forms (Smartforms and SapScript)

Dynamic Patterns – Let’s Automate the Documentation in SAP ABAP

$
0
0

Documentation is an important aspect of any Project deliverable which cannot be ignored. It not only helps in understanding the product better and developers can reuse the object better but also it proves to be the boon for the AMS team members who support and maintain your product long after you left the project. Every company has it’s own standard way to maintain comments/modification logs and documentations.

No doubt, documentation is necessary but developers feel it is a pain in the neck to write tons of non-executable comments/logs, just for someone else to have better readability.

Can we automate this so called non-productive work?

In this article, I would like to demonstrate a simple use case to use dynamic pattern and make the documentation process interactive and some fun. Also I would show how we can configure code template to enter multiple lines of snippet in a program by simply pressing Tab .

Just for the sake of clarity: Dynamic Pattern is nothing new. It has been there since SAP’s birth. I just did not find this topic at SAPYard, so thought of including it in our SAPYard repository.

I can’t provide the exact template (for obvious reasons) what I am using but you can add as many details as you want.

After calling the pattern in a program, it will first display a pop up containing all transports for that user and from which user can select the required TR as below.

Open your program – > click pattern -> other pattern -> enter your custom pattern


Press enter, pop up will display containing TR details

Normal logs will look like below –


And the dynamic code template will look like below –

Also Read: Lazy and Smart ABAPers

Above was a short demo of what this Pattern can do.

Now let’s start the development.

1. Dynamic Patterns

Open any program and go to Utilities -> More Utilities -> Create Pattern

Give name of the pattern 

Give name of the pattern  and press enter

Write *$&$MUSTER in the first line of the pattern and save.

Now we need a function group and a function module to call our dynamic pattern

Create a function group ( for example zfg_test1) and function module as <Pattern name>_editor_exit ( like ztest_pattern_editor_exit).

Note: <Pattern name>_editor_exit

Open Function module and enter BUFFER as Tables Parameter.


Declare your required variables.

DATA: comment1        TYPE buffer. 

Whatever data you want to display after calling the pattern should be passed in BUFFER table . 

For example to display transports –

Where ls_e070 contains the TR details.

I have used below code to add a pop up so that user can select the required TR.

LT_E070 – contain TR data, C_CHOICE table index of the selected TR.

After adding all the necessary code in the functional module , save and activate and call the pattern in your program and let the function module do all the magic. 🙂

Also CheckBack to Basics

2. Dynamic Re-usable Code Template

Now lets call a dynamic code template in the program and let’s add code just by pressing the button ‘TAB’.

Open any program and click the below highlighted template button 

Select code template -> add -> provide your template name (recommended to have the template name to be short usually 1-3 characters) -> press enter.

For our example we have named it ‘mod’.

Click insert tag -> select interactive -> user variable will display -> add your static comment to your template as below  -> save 

Open your program – > start typing “mod” -> press Tab. Please note, before you type mod, the helper would show mod and you can press Tab

Enter variable name like your SAP User Id or Name.

Press OK -> It will display like below. 

In this article, I just tried to show some basic stuffs which we can automate. You can create your template to write re-usable code for any BAPI call. You can automate to write comments for any lines.

You can also build the automation tool to create your own Technical Specification along with Table Access Diagram and Flow Charts.

Just use your imaginations and enjoy automation. 🙂 Please provide your feedback and thoughts in the comments section below.

If you have any tool(s) which helps the ABAPers, please do share.

We have a very active Telegram (App) SAP Technical Group with more than 3120+ SAP Technical Practitioners from 6 Continents of the SAP World. Please join it using below link.
Telegram SAP Technical Discuss Group. You need to install the Telegram App first on your mobile device. Once you have it on your mobile, you can join the group and also access it from the Web on your computer and laptop.

Check some Tweaks 

How to Troubleshoot Errors in SAP Applications using ANST?

$
0
0

Wondering what ANST is? Do not feel sad. You are not the only one who is hearing ANST for the first time. SAP is like a vast ocean and no one can claim to know every drop of that ocean. Just to make your heart lighter, even we did not know about ANST until few weeks ago. 🙂 ANST is the abbreviation for Automated Note Search Tool.

This article is intended to share awareness on the new supportability tools available in SAP to improve productivity while addressing standard issues through OSS notes in SAP applications.

Note: This article will be updated frequently with relevant supportability tools to help application developers troubleshoot SAP application issues.

Also Read: SAP Fiori Tutorial. Part VI. How to Troubleshoot SAP Fiori Errors?

Introduction

At times, searching SAP notes for any bugs or errors in an SAP application can be a time-consuming activity, and more often developers spend most of their time on below activities to identify the root cause.  

  • Search for most appropriate and relevant SAP note to fix the issue
  • Identify if any custom code has impacted the standard business functionality
  • Identify if any of the configuration table entries are missing
  • Identify possible objects from where you can start the debugging

With the introduction of new supportability tool like Automated Note Search Tool available from release SAP Basis release 700, efforts to identify OSS notes for any errors in standard or custom applications can be reduced drastically.

The tool not only helps you to identify the relevant OSS note, but also supports features to identify impact due to custom code, missing configuration entries and possible objects to start your debugging.

ANST Availability

Released for all support packages with release 740.

Software Component                        Release     Package name

  • SAP_BASIS                                        700           SAPKB70028
  • SAP_BASIS                                        701          SAPKB70113
  • SAP_BASIS                                        702           SAPKB70213
  • SAP_BASIS                                        731           SAPKB73106

The transaction to start the tool is named ANST_SEARCH_TOOL

After support package with note 1915529  the transaction has been renamed to ANST

Software Component                        Release     Package name

  • SAP_BASIS                                       700           SAPKB70030
  • SAP_BASIS                                       700           SAPKB70030
  • SAP_BASIS                                       701           SAPKB70115
  • SAP_BASIS                                       702           SAPKB70215
  • SAP_BASIS                                       731          SAPKB73111
  • SAP_BASIS                                       740          SAPKB74005

Pre-requisites before using the tool.

Authorizations required

  • Auth object: S_TCODE:                     Transaction ANST
  • Auth object: S_TCODE:                     Transaction SNOTE: For the note search
  • Auth object: S_DATASET:                Authorizations for file access
  • Auth object: S_ALV_LAYO:             for displaying/editing ALV lists
  • Auth object: S_GUI                            GUI activities (for example, download / upload)

SAP notes to be implemented

  • 2361155 – ANST: Change user on start & stop functionality
  • 2469123 – ANST: Remove identification of irrelevant SAP Notes
  • 2286869 – ANST: Trace On/Off error “Dynamic Start and Stop cancelled by user”

Also Check: Web Service in SAP – Part III – Testing & Investigation Tips

Overview on Automated Note Search Tool

The Automated Notes Search Tool (ANST) is an application that searches for SAP Notes in order to solve problems in SAP applications, specifically those SAP Notes that have correction instructions.

It helps SAP customers to find SAP Notes in order to solve a specific issue and works based on the issue replication at the customer system. When an issue is reproduced on the customer’s system, the list of application components that are involved is displayed and a note search is performed on the application component where the problem exists.

ANST

Key Features

  • Search for SAP notes
  • Customer code identification related to the bug
  • Identify missing customizing tables entries
  • Identify the possible objects from where debugging can be triggered.

How ANST works?

ANST works in 3 simple steps. 

  1. Identify an issue
  2. Replicate the issue through ANST
  3. Execute the trace to identify and implement the note

Step 1 – For any error reported in SAP application, depending on the support pack, execute transaction ANST or ANST_SEARCH_TOOL and then choose the most appropriate option to replicate the issue.

ANST – Replicate the issue

Step 2 – Once, the issue is replicated navigate back to ANST. A trace will be generated and grouped based on application component as shown below.

From here, you can either search for a Note, Custom Code or missing Customizing table entries that may be the potential reason for this issue.

In Our case, we will select the appropriate component and click on note search to identify the relevant SAP note.

ANST – Select Component and Search for the Note

Step 3 – Based on the component selected, list of relevant SAP notes will be displayed. After this, you need to identify the most appropriate SAP Note and implement it. You can display the note directly from here and also download the note if required.

Also WatchEnd to End Free Video Course on HANA, ABAP7.4, OData and Debugging

Additional Considerations while using ANST

  • ANST search will be optimize if there are a smaller number of objects, the search is based on objects presents inside the generated trace, so replicate the issue with minimal steps involved.
  • Maintain RFC destination SAPSNOTE in SM59 pointing to SAP OSS system, this is required by ANST and the search depends on the RFC connection. You maintain in the settings options available in ANST.
  • Ensure you read SAP Note before implementation if it really matches to your problematic scenario.
  • When using it for the first time and you get an error message: “Fatal Error. Customizing table is not filled.” Refer the SAP Note: 1909768.
  • When you are using ANST for a web application and you get an error message: “Web trace cancelled by the user.” Refer SAP Note: 1885730. After following this if the error still occurs then raise a ticket.
  • If you could not find any relevant SAP Note which can solve the issue, then report the case to SAP support by raising a ticket to BC-UPG-NA component.

And finally, I would like to end this blog by introducing the new exciting addition by ANST to Support of FIORI Apps:

  • This feature is available as of note 2605555 – ANST: Enhancement to support Fiori applications

To summarize, certainly, a good addition by SAP to improve productivity and expedite issue resolution in standard or custom applications by identifying and implementing OSS notes, custom program implications and missing configuration entries.

Do you have any other tool which you believe can help others. If yes, please do share it with us. Your feedback would help us create even better future articles. So, please share your thoughts in the comments section below.

Download Telegram App in your mobile and join the most active SAP Technical Discussion Group where we have dedicated members (3570+) from 6 continents.
Telegram SAP Technical Discuss Group. 

Free SAP ABAP for HANA Training Tutorials Exercises.

Trick to Send QR code or Barcode Data Remotely from Android to PC for SAPUI5 App

$
0
0

Even in this 21st Century, there are many small and medium sized organizations (SME – Small and Medium Enterprises) in developing countries who have relatively poor internet service and connectivity.

Such SMEs do not want to invest on expensive Wireless Scanner devices and Tablets to run Fiori apps (for obvious reasons). So what is the option? They want an economical and innovative provision to get remote Qrcode data into the PC Fiori app to save the cost and also meet the requirement.

How can we send the Qrcode data to the PC Fiori App remotely?

Solution:

Step 1: Install the Barcode to PC Desktop App – BarcodeToPC

Step 2: Execute the Desktop App


Step3:- Install the Android App on your mobile – Barcode to PC: Wi-Fi scanner

Also ReadABAP on SAP HANA. Part I. First Program in ABAP HANA

Step 4: Place the cursor on the input field of Fiori App and run the Android App you just downloaded and scan the Qr code from far place.

Pre-requisite – Both PC and Android should be connected to the same Wifi modem. You can scan any QR code material far away from PC to get it’s info to the PC. The person sitting in front of the PC will continue the transaction once scan is completed.

Sample Code for Qrcode Input in Fiori App

Also Check – Free End to End Video Courses on HANA, ABAP7.4+, OData, Debugging etc

For screenshot and demo purpose I am using a QR in view. In real time you just have an input and place your cursor on that input. Now ask someone with an Android phone to scan the material qr code.

Wola! You got the value on PC Fiori App.

Check some sample outputs below.

Hit single scan mode and scan the Qr.

The value would be populated in the Fiori App.

This is one of the real time applications we developed for one or our client. Innovation is the key to survive. With the advent of modern devices and technologies, doing business was never this easy.

As an application developer, it gives immense satisfaction to watch your development in live action. Happy clients make you a happy developer. 🙂

Do you have any such tips or tricks to share? Do send them to mail@sapyard.com.

Now, SAPYard has a YouTube Channel. Please Subscribe to our Channel for useful videos shorter than 5 minutes and for complete end to end free video courses.

We have a very active Telegram (App) SAP Technical Group with more than 3600+ SAP Technical Practitioners from 6 Continents of the SAP World. Please join it using this link. Telegram SAP Technical Discuss Group. 

You need to install the Telegram App first on your mobile device. Once you have it on your mobile, you can join the group and also access it from the Web on your computer and laptop.

Step by Step Tutorials on SAPUI5

How to Merge PDF Files using SAP.. absolutely Free?

$
0
0

Are you tired of using free online PDF merger tools available on internet with lots of unwanted advertisements and limitations? Ever tried merging PDFs using SAP? No worries this tutorial will help you to merge multiple PDF using SAP.

There can be 3 common possibilities to merge PDFs:

  1. Merging two PDFs from external source
  2. Merging two SAP Adobe forms
  3. Merging an SAP Adobe form and a PDF from an external source. Note: ADS doesn’t support merging PDFs yet, so its tricky one.

A. Merging two PDFs from external source

First case is pretty simple and we have a standard SAP program to help us. Program – RSPO_TEST_MERGE_PDF_FILES.

Just you need to select the PDFs and execute the program and Ta-Da! 
your merged file is ready.

Also Read: How I used SAP Adobe Form as my personal PDF editor?

B. Merging two SAP Adobe Forms or Merging an SAP Adobe Form and a PDF From an external source.

Now comes the tricky part. How to merge two or more SAP Adobe forms or an Adobe form with an external PDF?

For this we have PDFTK(PDF Tool Kit) as our savior. It is an external tool. You need some help from Basis and Security team to install the tool and get the required authorizations. So have good terms with your Basis and Security friends.. SAP is a team game and you cannot win it alone. 🙂

Let’s begin merging..

Here are steps that you need to perform before writing your code:

  1. Install PDFTK and the required class – Please go through the attached link and perform the steps as given. Merge PDF files in ABAP
  2. Create class ZCL_PDF_MERGE using the SAP PDF Merge Nugget
  3. Write the driver program to implement this functionality.

Below are the steps to be performed in the driver program:

  1. Get all the PDF content (ADS, PDF from presentation server, PDF from application server, PDF from DMS server or any other source) in XSTRING format.
  2. Append all the XSTRING PDFs into one internal table IT_PDF. Loop IT_PDF and call ADD_PDF() method of the class ZCL_PDF_MERGE . This will create temporary PDFs in your application server path ZPDFMERGE created by BASIS team.
  3. Finally, call GET_MERGED() method which will create the final merged PDF and return the final PDF XSTRING in the variable v_pdf_merged, also it will delete all the temporary files those were created above.
  4. Now you have the final merged PDF data in XSTRING format and you can use it as per your requirement (to send mails, to save in presentation server or application server , to show on the SAP screen just like an adobe form output).
  5. Below is an example code snippet to merge two adobe form with a PDF from the application server and then save it in the presentation server.

Also Check:How to Email Smartform as PDF Attachment to Multiple Users?

Driver Program Code Snippet:

Prerequisites- Authorization object S_LOG_COM should be assigned to you and also you should have ZPDFMERGE path access.

REPORT zsapyard_pdf_merge.
PARAMETERS : p_path TYPE sdok_filnm. ” app server path
DATA: lo_pdfmerge      TYPE REF TO zcl_pdf_merge,
      v_pdf_merged     TYPE xstring,
      v_pdf            TYPE xstring,
      it_pdf           TYPE STANDARD TABLE OF xstring,
      wa_formoutput1   TYPE fpformoutput,
      wa_outputparams  TYPE sfpoutputparams,
      wa_docparams     TYPE sfpdocparams,
      wa_formoutput2   TYPE fpformoutput,
      v_formname1      TYPE fpname,
      v_formname2      TYPE fpname,
      v_function_name1 TYPE rs38l_fnam,
      v_function_name2 TYPE rs38l_fnam,
      bin_tab          TYPE STANDARD TABLE OF tabl1024,
      lo_gui           TYPE REF TO cl_gui_frontend_services,
      path             TYPE string,
      fullpath TYPE string,
      length           TYPE i,
      filter           TYPE string,
      uact             TYPE i,
      name             TYPE string.

*first adobe form.
v_formname1 = ‘ZTEST1’.

*get function module names for Adobe forms
TRY.
    CALL FUNCTION ‘FP_FUNCTION_MODULE_NAME’
      EXPORTING
        i_name     = v_formname1
      IMPORTING
        e_funcname = v_function_name1.
*         e_interface_type = lv_interface_type1.

  CATCH cx_fp_api_internal
        cx_fp_api_repository
        cx_fp_api_usage.
*      MOVE sy-subrc TO lv_subrc.
ENDTRY.

* second adobe form
v_formname2 = ‘ZTEST2’.
TRY.
    CALL FUNCTION ‘FP_FUNCTION_MODULE_NAME’
      EXPORTING
        i_name     = v_formname2
      IMPORTING
        e_funcname = v_function_name2.
*           e_interface_type = lv_interface_type2.

  CATCH cx_fp_api_internal
        cx_fp_api_repository
        cx_fp_api_usage.
*      MOVE sy-subrc TO lv_subrc.
ENDTRY.

wa_outputparams-nodialog = ‘X’.
wa_outputparams-preview = abap_true.
wa_outputparams-reqnew = ‘X’.
wa_outputparams-nopdf = ‘X’.
wa_outputparams-arcmode = ‘1’.

* Set parameters (print/send/output device etc)
CALL FUNCTION ‘FP_JOB_OPEN’
  CHANGING
    ie_outputparams = wa_outputparams
  EXCEPTIONS
    cancel          = 1
    usage_error     = 2
    system_error    = 3
    internal_error  = 4
    OTHERS          = 5.
IF sy-subrc <> 0.
*    MOVE sy-subrc TO lv_subrc.
ENDIF.

wa_docparams-langu = ‘E’.
wa_docparams-replangu2 = ‘E’.
CALL FUNCTION v_function_name1
  EXPORTING
    /1bcdwb/docparams  = wa_docparams
  IMPORTING
    /1bcdwb/formoutput = wa_formoutput1    “1st adobe form
  EXCEPTIONS
    usage_error        = 1
    system_error       = 2
    internal_error     = 3
    OTHERS             = 4.
IF sy-subrc <> 0.
*    MOVE sy-subrc TO lv_subrc.
ENDIF.
CALL FUNCTION v_function_name2
  EXPORTING
    /1bcdwb/docparams  = wa_docparams
  IMPORTING
    /1bcdwb/formoutput = wa_formoutput2     “2nd adobe form
  EXCEPTIONS
    usage_error        = 1
    system_error       = 2
    internal_error     = 3
    OTHERS             = 4.
IF sy-subrc <> 0.
*    MOVE sy-subrc TO lv_subrc.
ENDIF.

*get external source PDF data from the application server
OPEN DATASET p_path FOR INPUT IN BINARY MODE.
IF sy-subrc EQ 0.
  READ DATASET p_path INTO v_pdf.
  IF sy-subrc EQ 0.
  ENDIF.
ENDIF.
CLOSE DATASET p_path.

* create table with all xstring data
APPEND v_pdf TO it_pdf.
v_pdf = wa_formoutput1-pdf.
APPEND v_pdf TO it_pdf.
v_pdf = wa_formoutput2-pdf.
APPEND v_pdf TO it_pdf.

* use ZCL_PDF_MERGE class to get the PDF xstring after merging all PDF’s
CREATE OBJECT lo_pdfmerge.
LOOP AT it_pdf INTO v_pdf.
  lo_pdfmerge->add_pdf( v_pdf ).
ENDLOOP.
v_pdf_merged = lo_pdfmerge->get_merged( ). “final merged data
*download merged PDF to your desktop
CREATE OBJECT lo_gui.

CALL FUNCTION ‘SCMS_XSTRING_TO_BINARY’
  EXPORTING
    buffer        = v_pdf_merged
  IMPORTING
    output_length = length
  TABLES
    binary_tab    = bin_tab.

CALL METHOD lo_gui->file_save_dialog
  EXPORTING
    default_extension = ‘pdf’
    default_file_name = ‘merged.pdf’
    file_filter       = filter
  CHANGING
    filename          = name
    path              = path
    fullpath          = fullpath
    user_action       = uact.
IF uact = lo_gui->action_cancel.
  EXIT.
ENDIF.

lo_gui->gui_download( EXPORTING
                        filename = fullpath
                        filetype = ‘BIN’
                        bin_filesize = length
                      CHANGING
                        data_tab = bin_tab ).

And, your merged file is ready. You can also create a spool request for this or show this on your screen as per your requirement. 🙂

We have a very active Telegram (App) SAP Technical Group with more than 3600+ SAP Technical Practitioners from 6 Continents of the SAP World. Please join it using below link.
Telegram SAP Technical Discuss Group. You need to install the Telegram App first on your mobile device. Once you have it on your mobile, you can join the group and also access it from the Web on your computer and laptop.

Check some Tweaks 


Web Service in SAP – Part IV – XSD Schema Error in External Web Service URL

$
0
0

Many times when we try to create Consumer Proxy for the external Web Service (URL), we get error while accessing the WSDL/Schema. The error can be because of firewall, trusted network, URL certification or authorization issues. But once all the network and authorization issues are resolved, if you still get error in WSDL/Schema, then it is highly possible that your Web Service URL has the link to Schema Location which it cannot identify at runtime wizard while creating the Consumer Proxy.

Also Read: How to Create, Consume and Troubleshoot SAP Web Service Consumer?

Say you are trying to create consumer proxy in SE80 like shown below.

You give the name and proceed with the Enterprise Services Create Wizard.

But you are stuck in the step “Enter the URL for accessing the WSDL/Schema”.

Errors can be like below: “500 Internal Server Error”.

Error can be like “500 SSL Peer Certificate Untrusted”.

Error in URL accessing the WSDL/Schema can also be for .XSD like below.

Sample xsd errors in WSDL URL:

“Error while uploading the xsd file(schema) in web services definition”

“File ContractService.xsd was not attached because it has an invalid file extension not allowed by the system settings”

“Exception occurred in communication framework: Error in HTTP Framework:500Internal Server Error”

“…<WSDL URL>.xsd “ error

by the way XSD means XML Schema Definition

Most of the time, WSDL URL has the hyper link (URL) to the schema location. Instead of the <xsd:schema> definition </xsd:schema>, it has a URL link to schema location where we can see the actual schema definition and properties. In such Web Services, if we try to consume the WSDL URL, we get error. In order to avoid that error, we need to use the WSDL local file. Do not worry, the local WSDL file would be dynamic and would work exactly like the WSDL URL. But we need to do a small tweak in the local WSDL file.

We need to replace the link to schema location with the exact schema definition from the schema location URL in the WSDL local file. Once we have the complete schema definition replaced in the WSDL, we should be able to create the consumer proxy using the local WSDL file.

Also Read: How to Create Web Service in SAP and configure SOAMANANGER?

Below steps would demonstrate in details, how we can replace the xsd schema location with the exact xsd schema definition. We have taken the public dnb toolkit web service for our example.

Step 1 – Refer to below link for dnb WSDL

https://toolkit-api.dnb.com/locked/WSDLEndpoints.asp?stat_link=020201&id=70BCF32F829359890238

We are referring to GDP_V4.wsdl.

Step 2 – Open the Web Service

https://toolkit-api.dnb.com/ws/DNB_WebServices.Providers.OrderAndInvestigations.GDP_V4:wsp_GDP_V4?WSDL

Step 3 – Save the Web Service as .txt file

Save the WSDL file as .txt in your local machine. Screenshot of the txt file opened in wordpad below.

Step 4 – Look for <xsd:schema> Key Word

Take a look at the <xsd:schema> either in the txt file (screenshot above) or on WSDL in browser (screenshot below).

<xsd:schema> </xsd:schema>

Step 5 – Expand <xsd:schema> </xsd:schema>

<xsd:schema><xsd:import namespace=”http://www.dnb.com/DNB_WebServices/Providers/OrderAndInvestigations/GDP_V4/wsp_GDP_V4” schemaLocation=”https://toolkit-wsdl.dnb.com/ws/DNB_WebServices.Providers.OrderAndInvestigations.GDP_V4:wsp_GDP_V4?WSDL&fingerprint=P6DCJGGOL7NVDOIVFGQWX7EKOTZNG45M.xsd” /></xsd:schema>

Step 6 – Look for schemaLoation Key Word and schemaLocation Path

For this WSDL the schemaLocation path is:

https://toolkit-wsdl.dnb.com/ws/DNB_WebServices.Providers.OrderAndInvestigations.GDP_V4:wsp_GDP_V4?WSDL&fingerprint=P6DCJGGOL7NVDOIVFGQWX7EKOTZNG45M.xsd

Step 7 – Replace the xsd:schema in the WSDL file with the exact definition from the schemaLocation.

Open the schemaLocation Path URL in another Tab in the browser. It will give the full Type definition of the xsd:schema.

Do not forget to expand all <xsd:sequence> before you copy the full definition.

Once it is expanded, it is no more yellow.

The easy way to expand all the sequence is by going to page source. Right click on any area and go to View page source

Also Read: Web Service in SAP – Part III – Testing & Investigation Tips

Step 8 – Copy XSD Schema Definition and Replace

Copy the full xsd:schema and replace in the original WSDL.txt file with the xsd:schema from the schemaLocation.

PS: Do not copy the first line of the page source (as marked in image above).

Tips: <xsd:schema> </xsd:schema> should be between <wsdl:types> </wsdl:types>.

Step 9 – Save the Web Service file with .WSDL extension

Sample .WSDL image

Step 10 – Choose the WSDL Source as Local File in the Wizard

Now, wherever you were trying to use the WSDL URL, replace it with your local WSDL file. The xsd schema error should be resolved.

Screenshot of sample Consumer Proxy we created.

This should fix the Web Service XSD Schema issue.

Thank you Mastan Raju!!

A special thanks to Mastan Raju, who helped us with a video explaining the root cause. He helped us figuring out the issue and even sent us a sample WSDL file. With his help, we were able to create the consumer proxy from local WSDL file and consume the external Web Service.

If you want to have real time discussions and resolutions, do join our SAP Technical Telegram Group where we have more than 4670+ active SAP consultants from 6 Continents.

Please Note: You need to install Telegram App on your mobile first and then you can join the group using the above link.

Please SUBSCRIBE to SAPYard’s Youtube Channel for Free End to End SAP Video Course and Training.

Step by Step Tutorials on Web Services in SAP

A to Z of OLE Excel in ABAP 7.4

$
0
0

SAP users, both business and end users always need to download the output of a report to spreadsheet and do their analytics. The standard excel output from a report is very simple process but it is old fashioned and the spreadsheet looks quite boring. There is no default formatting and the users have to do all the hard work of changing the fonts, coloring the texts, marking the borders etc.

I acknowledge, whatever I mentioned above can be achieved in many ways programmatically. We can do it in the old traditional ABAP way but providing multiple tabs in the spreadsheet and formatting is quite tricky with non OLE method.

OLE = Object Linking and Embedding

The high level agenda of this article is to be the G.O.A.T. (please google it if you do not know the full form) of OLE Excel Guide. We want this one article to be the light house of all ABAP developers who needs to work with OLE Excel in their projects. Too much of chest thumping before the start. Right? 😉

We would use the new ABAP 7.4+ syntaxes to created Excel with OLE. We would format the spreadsheet. Headers in bold, font color would be different at different area, background would be blue or any color of your choice. We would mark the borders better and also create multiple Tabs in the excel spreadsheet.

Also Read: Create your first OData Service

Let’s start our dive into the OLE approach of creating Excel.

Step 1 – Include OLE2INCL in the program

The OLE automation programs “OLE2INCL” include needs to be specified. It is kind of a library of OLE syntax.

<code>*** Include objects
INCLUDE: ole2incl.</code>

Step 2 – Populate the internal tables with data

For our demo, we are fetching data from some standard SAP tables so that everyone can use our code snippet.

<code>*** Popoluate tables
SELECT ebeln,
       bukrs,
       bstyp,
       bsart
  INTO TABLE @DATA(gt_ekko)
  FROM ekko
  UP TO 5 ROWS
  WHERE ebeln LIKE '45%'.

IF gt_ekko[] IS NOT INITIAL.

  SELECT ebeln,
         ebelp,
         matnr,
         bukrs
    INTO TABLE @DATA(gt_ekpo)
    FROM ekpo
    FOR ALL ENTRIES IN @gt_ekko
    WHERE ebeln = @gt_ekko-ebeln.

  IF gt_ekpo[] IS NOT INITIAL.
    SELECT ebeln,
           ebelp,
           gjahr,
           belnr
      INTO TABLE @DATA(gt_ekbe)
      FROM ekbe
      FOR ALL ENTRIES IN @gt_ekpo
      WHERE ebeln = @gt_ekpo-ebeln
      AND   ebelp = @gt_ekpo-ebelp.
  ENDIF.
ENDIF.</code>

We have 3 internal tables viz GT_EKKO, GT_EKPO and GT_EKBE ready.

Step 3 – Put the internal table in a string table separated by delimiter

Our goal is to put each internal table data in different tabs of the excel. So we would save the internal table data in a long character variable where the data would be separated by delimiter ‘|’. The long variable would be used later to create a file at run-time and save in the tabs of the spreadsheet.

For our explanation, we have created a Table Type with 1500 long characters and used it for storing data separated by ‘|’. I have defined the delimiter variable as ‘deli’ which contains ‘|’ value ( cl_abap_char_utilities=>horizontal_tab ).

Did you think deli was for food? 🙂

<code>TYPES: data1(1500) TYPE c,
       ty_data     TYPE TABLE OF data1.

*** Variables
DATA: gt_1              TYPE ty_data WITH HEADER LINE,
      gt_2              TYPE ty_data WITH HEADER LINE,
      gt_3              TYPE ty_data WITH HEADER LINE,
      deli(1)           TYPE c.

"Delimeter
deli = cl_abap_char_utilities=>horizontal_tab.</code>

Step 4 – Put internal table data to respective data/file types

Loop through the internal tables and concatenate the data to the respective string variables (gt_1, gt_2, gt_3). Please note, I have passed the headers as well, which will act like column names in excel sheets.

Check the new syntax for concatenation. If you are new to ABAP 7.4 syntax, please take the Free End to End Video Course on ABAP 7.4 Syntax and New Features.

<code>**Header for first sheet
gt_1 = |EBELN{ deli }BUKRS{ deli }BSTYP{ deli }BSART|.
APPEND gt_1.
CLEAR gt_1.

**Data for first sheet
LOOP AT gt_ekko INTO DATA(wa_ekko).
  gt_1 = | { wa_ekko-ebeln } { deli } { wa_ekko-bukrs } { deli } { wa_ekko-bstyp } { deli } { wa_ekko-bsart } |.
  APPEND gt_1.
  CLEAR gt_1.
ENDLOOP.

**Header for second sheet
gt_2 = |EBELN{ deli }EBELP{ deli }MATNR{ deli }BUKRS|.
APPEND gt_2.
CLEAR gt_2.

**Data for second sheet
LOOP AT gt_ekpo INTO DATA(wa_ekpo).
  gt_2 = | { wa_ekpo-ebeln } { deli } { wa_ekpo-ebelp } { deli } { wa_ekpo-matnr } { deli } { wa_ekpo-bukrs } |.
  APPEND gt_2.
  CLEAR gt_2.
ENDLOOP.

**Header for third sheet
gt_3 = |EBELN{ deli }EBELP{ deli }GJAHR{ deli }BELNR|.
APPEND gt_3.
CLEAR gt_3.

**Data for third sheet
LOOP AT gt_ekbe INTO DATA(wa_ekbe).
  gt_3 = | { wa_ekbe-ebeln } { deli } { wa_ekbe-ebelp } { deli } { wa_ekbe-gjahr } { deli } { wa_ekbe-belnr } |.
  APPEND gt_3.
  CLEAR gt_3.
ENDLOOP.</code>

Step 5 – Time for OLE Application

Create an OLE object as shown below.

<code>* start Excel
  CREATE OBJECT h_excel 'EXCEL.APPLICATION'.</code>

Step 6 – Create Workbook and Worksheets

If you are struggling (I hope not) with the concepts of workbook and worksheets then this snapshot should help.

We are going to use all these functionalities in our OLE generation report. Excited?? 😛

Let us create Workbook and Worksheets.

<code>*--- get list of workbooks, initially empty
CALL METHOD OF h_excel 'Workbooks' = h_sheets.

SET PROPERTY OF h_excel 'Visible' = 1. “If ‘1’ – it opens excel application in frontend and if ‘0’ then excel will be created in backend mode.

CALL METHOD OF h_sheets 'Add' = h_sheet.</code>

All the objects of excel application must be declare with type ‘ole2_object’.

<code>* Ole data Declarations
DATA: h_excel   TYPE ole2_object, " Excel object
      h_sheets  TYPE ole2_object, " list of workbooks
      h_sheet   TYPE ole2_object, " workbook
      h_cell    TYPE ole2_object, " cell
      worksheet TYPE ole2_object, "Worksheet
      e_color   TYPE ole2_object, "Color
      range     TYPE ole2_object, "Range
      borders   TYPE ole2_object, "Borders
      h_sheet1  TYPE ole2_object, "First sheet
      h_sheet2  TYPE ole2_object, "Second Sheet
      h_sheet3  TYPE ole2_object, "Third Sheet
      gs_font   TYPE ole2_object. "Font</code>

Also Check: An ABAPer’s First SAPUI5 App in SAP WebIDE

Step 7 – Activate the current worksheet and name it

<code>GET PROPERTY OF h_excel 'ACTIVESHEET' = worksheet.

SET PROPERTY OF worksheet 'Name' = ’EKKO’. “Sheet name</code>

Step 8 – Pass the data from string internal table to Excel file

There are two ways to pass the data in excel:
i) one-by-one
ii) copy-paste method

Here, we am going to copy whole data from internal table and paste it in the excel. This approach saves time and increases the performance of code. See, we revealed a way to optimize the code. 😛

<code>**Copy data in clipboard
  CALL METHOD cl_gui_frontend_services=>clipboard_export
    IMPORTING
      data                 = gt1[]
    CHANGING
      rc                   = l_rc
    EXCEPTIONS
      cntl_error           = 1
      error_no_gui         = 2
      not_supported_by_gui = 3
      OTHERS               = 4.</code>

The above snippet is self explanatory. It copies the data of internal table into clipboard.

Ctrl C is always followed by Ctrl V. 🙂

Now paste the copied data from clipboard to spreadsheet.

For pasting the copied data in excel sheet, we need to select the cells and need to make the range, in which the data will be pasted.

<code>**choose first cell.
  CALL METHOD OF h_excel 'Cells' = w_cell1
    EXPORTING
    #1 = 1 "Row
    #2 = 1. "Column

**choose second cell.
  CALL METHOD OF h_excel 'Cells' = w_cell2
    EXPORTING
    #1 = 1 "Row
    #2 = 1. "Column

**Make range from selected cell
  CALL METHOD OF h_excel 'Range' = range
    EXPORTING
    #1 = w_cell1
    #2 = w_cell2.</code>

In our program, we have EBELN as our first field in every table. After copying that data into excel sheet, we see EBELN in below format because of space constraints (less width of cell).

Change the width of particular column with property ‘Columnwidth’.

<code>**Change width of column.
  SET PROPERTY OF w_cell1 'Columnwidth' = 12.</code>

Now we need to select the range and paste it in excel worksheet.

<code>  CALL METHOD OF range 'Select'.
** Paste data from clipboard to worksheet.
  CALL METHOD OF worksheet 'Paste'.</code>

Step 9 – Formatting of the Excel Spreadsheet in SAP ABAP

The above steps ensure, we have the data in our excel. Now we have the interesting job to do i.e. Formatting.

9.1 Create Borders

Whatever data we are going to paste in excel should contains borders. For achieving this, Excel application has a property as ‘borders’.

<code>CALL METHOD OF range 'Borders' = borders NO FLUSH
     EXPORTING #1  = 7. "7 for left side
SET PROPERTY OF borders 'LineStyle'= 1. "type of line.</code>

Above, 7 is indicating border for left side. Same way we have,
8 for right side, 9 for top side, etc.

<code>**Logic to assign borders to fetched data in worksheet.
  DATA(i) = 0.
  LOOP AT it_sheet INTO DATA(ls_sheet).
    i = i + 1.
    DATA(first) = |A{ i }|. "Column from where you want to start providing borders.
    DATA(second) = |D{ i }|. "Column up to which you want to provide the borders.

**Make range of selected columns.
    CALL METHOD OF h_excel 'Range' = range
      EXPORTING
      #1 = first
      #2 = second.

**Logic to assign border on left side.
    CALL METHOD OF range 'Borders' = borders    NO FLUSH
     EXPORTING #1  = 7. "7 for left side
    SET PROPERTY OF borders 'LineStyle'= 1. "type of line.

**Logic to assign border on right side.
    CALL METHOD OF range 'Borders' = borders    NO FLUSH
       EXPORTING #1  = 8.
    SET PROPERTY OF borders 'LineStyle'= 1.

**Logic to assign border on top side.
    CALL METHOD OF range 'Borders' = borders    NO FLUSH
       EXPORTING #1  = 9.
    SET PROPERTY OF borders 'LineStyle'= 1.

**Logic to assign border on bottom side.
    CALL METHOD OF range 'Borders' = borders    NO FLUSH
       EXPORTING #1  = 10.
    SET PROPERTY OF borders 'LineStyle'= 1.

**Logic to assign border on vertical side.
    CALL METHOD OF range 'Borders' = borders    NO FLUSH
       EXPORTING #1  = 11.
    SET PROPERTY OF borders 'LineStyle'= 1.

**Logic to assign border on horizontal side.
    CALL METHOD OF range 'Borders' = borders    NO FLUSH
       EXPORTING #1  = 12.
    SET PROPERTY OF borders 'LineStyle'= 1.

  ENDLOOP.</code>

Also Check : How to export a formatted Excel file with colors, borders, filters in Web Dynpro.

9.2 Change the Font and Color of Header cells

For our case, we have only 4 headers in every worksheet. Therefore we have hard coded for 4 headers. You should make it dynamic.

<code>** Logic to change font and color of header data.
  CLEAR i.
  DO 4 TIMES.
    i = i + 1.
    CALL METHOD OF h_excel 'CELLS'= h_cell  NO FLUSH
       EXPORTING #1 = 1
                 #2 = i.

    GET PROPERTY OF h_cell 'FONT' = gs_font NO FLUSH.

    SET PROPERTY OF gs_font 'BOLD' = 1 NO FLUSH.

    SET PROPERTY OF gs_font 'SIZE' = 12.

    CALL METHOD OF h_cell 'INTERIOR' = range.

    SET PROPERTY OF range 'ColorIndex' = 5.

    SET PROPERTY OF range 'Pattern' = 1.
  ENDDO.</code>

Above code is pretty simple. But you may want to understand the property names. 😊 For different colors, we have different numbers in OLE as shown below:

Choose your favourite color and you are done.

Check the output of the three Tabs.

Isn’t this cool? 😉 You can play with the excel application and it’s properties to explore more. Next time create your own excel output, format it with the eyes of a designer and amaze your client with the beautiful presentation. 😊 After all, SAP is not that boring as others complain.

The complete end to end code snippet is below at the end of the article. You may copy and past it in your ABAP editor. It should work without any hiccups.

This is the first time I am writing any blog in any space. Hopefully with each new articles, I would learn the tricks of writing better tutorials and learning series. SAPYard inspired me to be a technical author and I thoroughly enjoyed the process, right from conceptualizing the topic, working on the actual program to present and actually implementing my writing skills. If you want to join the Awesome SAPYard Author’s team, feel free to register or you may submit your articles directly to mail@sapyard.com or mailsapyard@gmail.com with all details (text, images and code) and SAPYard team would publish it in your name.

Please leave your feedback for my first article.

Please SUBSCRIBE to SAPYard’s Youtube Channel for Free End to End SAP Video Tutorial and Training.

If you want to have real time discussions and resolutions, do join our SAP Technical Telegram Group where we have more than 4675+ active SAP consultants from 6 Continents.

Please Note: You need to install Telegram App on your mobile first and then you can join the group using the above link.

Step by Step Tutorials on OOPs ABAP

<code>**&amp;---------------------------------------------------------------------*
**&amp; Report ZEXCEL_TAB
**&amp;---------------------------------------------------------------------*
**&amp;
**&amp;---------------------------------------------------------------------*
REPORT zexcel_tab.

*&amp;---------------------------------------------------------------------*
*&amp; Include Z_EXCEL_DOWNLOAD_TOP   Report Z_EXCEL_DOWNLOAD
*&amp;---------------------------------------------------------------------*

*** Include objects
INCLUDE: ole2incl.

TYPES: data1(1500) TYPE c,
       ty_data     TYPE TABLE OF data1.

*** Variable Declarations
DATA: w_cell1 TYPE ole2_object,
      w_cell2 TYPE ole2_object.

* Ole data Declarations
DATA: h_excel   TYPE ole2_object, " Excel object
      h_sheets  TYPE ole2_object, " list of workbooks
      h_sheet   TYPE ole2_object, " workbook
      h_cell    TYPE ole2_object, " cell
      worksheet TYPE ole2_object, "Worksheet
      e_color   TYPE ole2_object, "Color
      range     TYPE ole2_object, "Range
      borders   TYPE ole2_object, "Borders
      h_sheet1  TYPE ole2_object, "First sheet
      h_sheet2  TYPE ole2_object, "Second Sheet
      h_sheet3  TYPE ole2_object, "Third Sheet
      gs_font   TYPE ole2_object. "Font

*** Variables
DATA: gt_1              TYPE ty_data WITH HEADER LINE,
      gt_2              TYPE ty_data WITH HEADER LINE,
      gt_3              TYPE ty_data WITH HEADER LINE,
      deli(1)           TYPE c,
      gv_sheet_name(20) TYPE c.

DATA l_rc TYPE i.

"Delimeter
deli = cl_abap_char_utilities=>horizontal_tab.

*** Popoluate tables
SELECT ebeln,
       bukrs,
       bstyp,
       bsart
  INTO TABLE @DATA(gt_ekko)
  FROM ekko
  UP TO 5 ROWS
  WHERE ebeln LIKE '45%'.

IF gt_ekko[] IS NOT INITIAL.

  SELECT ebeln,
         ebelp,
         matnr,
         bukrs
    INTO TABLE @DATA(gt_ekpo)
    FROM ekpo
    FOR ALL ENTRIES IN @gt_ekko
    WHERE ebeln = @gt_ekko-ebeln.

  IF gt_ekpo[] IS NOT INITIAL.
    SELECT ebeln,
           ebelp,
           gjahr,
           belnr
      INTO TABLE @DATA(gt_ekbe)
      FROM ekbe
      FOR ALL ENTRIES IN @gt_ekpo
      WHERE ebeln = @gt_ekpo-ebeln
      AND   ebelp = @gt_ekpo-ebelp.
  ENDIF.
ENDIF.

**Header for first sheet
gt_1 = |EBELN{ deli }BUKRS{ deli }BSTYP{ deli }BSART|.
APPEND gt_1.
CLEAR gt_1.

**Data for first sheet
LOOP AT gt_ekko INTO DATA(wa_ekko).
  gt_1 = | { wa_ekko-ebeln } { deli } { wa_ekko-bukrs } { deli } { wa_ekko-bstyp } { deli } { wa_ekko-bsart } |.
  APPEND gt_1.
  CLEAR gt_1.
ENDLOOP.

**Header for second sheet
gt_2 = |EBELN{ deli }EBELP{ deli }MATNR{ deli }BUKRS|.
APPEND gt_2.
CLEAR gt_2.

**Data for second sheet
LOOP AT gt_ekpo INTO DATA(wa_ekpo).
  gt_2 = | { wa_ekpo-ebeln } { deli } { wa_ekpo-ebelp } { deli } { wa_ekpo-matnr } { deli } { wa_ekpo-bukrs } |.
  APPEND gt_2.
  CLEAR gt_2.
ENDLOOP.

**Header for third sheet
gt_3 = |EBELN{ deli }EBELP{ deli }GJAHR{ deli }BELNR|.
APPEND gt_3.
CLEAR gt_3.

**Data for third sheet
LOOP AT gt_ekbe INTO DATA(wa_ekbe).
  gt_3 = | { wa_ekbe-ebeln } { deli } { wa_ekbe-ebelp } { deli } { wa_ekbe-gjahr } { deli } { wa_ekbe-belnr } |.
  APPEND gt_3.
  CLEAR gt_3.
ENDLOOP.

* start Excel
IF h_excel-header = space OR h_excel-handle = -1.
  CREATE OBJECT h_excel 'EXCEL.APPLICATION'.
ENDIF.



*--- get list of workbooks, initially empty
CALL METHOD OF h_excel 'Workbooks' = h_sheets.
SET PROPERTY OF h_excel 'Visible'       = 1.
CALL METHOD OF h_sheets 'Add' = h_sheet.

PERFORM create_sheet TABLES gt_1
                     USING 'EKKO' h_sheet1.

PERFORM create_sheet TABLES gt_2
                     USING 'EKPO' h_sheet2.

PERFORM create_sheet TABLES gt_3
                     USING 'EKBE' h_sheet3.

* Free Excel objects
FREE OBJECT: h_cell,
             h_sheets,
             h_sheet,
             h_excel.

*&amp;---------------------------------------------------------------------*
*&amp;      Form  CREATE_SHEET
*&amp;---------------------------------------------------------------------*
FORM create_sheet  TABLES   it_sheet TYPE ty_data
                   USING    iv_name
                            iv_sheet TYPE ole2_object.

  DATA l_rc TYPE i.

  gv_sheet_name = iv_name.

  IF gv_sheet_name NE 'EKKO'.
    GET PROPERTY OF h_excel 'Sheets' = iv_sheet .

    CALL METHOD OF iv_sheet 'Add' = h_sheet.

    SET PROPERTY OF h_sheet 'Name' = gv_sheet_name .

    GET PROPERTY OF h_excel 'ACTIVESHEET' = worksheet.
  ELSE.
    GET PROPERTY OF h_excel 'ACTIVESHEET' = worksheet.

    SET PROPERTY OF worksheet 'Name' = gv_sheet_name .
  ENDIF.

**Copy data in clipboard
  CALL METHOD cl_gui_frontend_services=>clipboard_export
    IMPORTING
      data                 = it_sheet[]
    CHANGING
      rc                   = l_rc
    EXCEPTIONS
      cntl_error           = 1
      error_no_gui         = 2
      not_supported_by_gui = 3
      OTHERS               = 4.

**choose first cell.
  CALL METHOD OF h_excel 'Cells' = w_cell1
    EXPORTING
    #1 = 1 "Row
    #2 = 1. "Column

**choose second cell.
  CALL METHOD OF h_excel 'Cells' = w_cell2
    EXPORTING
    #1 = 1 "Row
    #2 = 1. "Column

**Change width of column.
  SET PROPERTY OF w_cell1 'Columnwidth' = 12.

**Make range from selected cell
  CALL METHOD OF h_excel 'Range' = range
    EXPORTING
    #1 = w_cell1
    #2 = w_cell2.

  CALL METHOD OF range 'Select'.
** Paste data from clipboard to worksheet.
  CALL METHOD OF worksheet 'Paste'.

**Logic to assign borders to fetched data in worksheet.
  DATA(i) = 0.
  LOOP AT it_sheet INTO DATA(ls_sheet).
    i = i + 1.
    DATA(first) = |A{ i }|. "Column from where you want to start providing borders.
    DATA(second) = |D{ i }|. "Column up to which you want to provide the borders.

**Make range of selected columns.
    CALL METHOD OF h_excel 'Range' = range
      EXPORTING
      #1 = first
      #2 = second.

**Logic to assign border on left side.
    CALL METHOD OF range 'Borders' = borders    NO FLUSH
     EXPORTING #1  = 7. "7 for left side
    SET PROPERTY OF borders 'LineStyle'= 1. "type of line.

**Logic to assign border on right side.
    CALL METHOD OF range 'Borders' = borders    NO FLUSH
       EXPORTING #1  = 8.
    SET PROPERTY OF borders 'LineStyle'= 1.

**Logic to assign border on top side.
    CALL METHOD OF range 'Borders' = borders    NO FLUSH
       EXPORTING #1  = 9.
    SET PROPERTY OF borders 'LineStyle'= 1.

**Logic to assign border on bottom side.
    CALL METHOD OF range 'Borders' = borders    NO FLUSH
       EXPORTING #1  = 10.
    SET PROPERTY OF borders 'LineStyle'= 1.

**Logic to assign border on vertical side.
    CALL METHOD OF range 'Borders' = borders    NO FLUSH
       EXPORTING #1  = 11.
    SET PROPERTY OF borders 'LineStyle'= 1.

**Logic to assign border on horizontal side.
    CALL METHOD OF range 'Borders' = borders    NO FLUSH
       EXPORTING #1  = 12.
    SET PROPERTY OF borders 'LineStyle'= 1.

  ENDLOOP.

** Logic to change font and color of header data.
  CLEAR i.
  DO 4 TIMES.
    i = i + 1.
    CALL METHOD OF h_excel 'CELLS'= h_cell  NO FLUSH
       EXPORTING #1 = 1
                 #2 = i.

    GET PROPERTY OF h_cell 'FONT' = gs_font NO FLUSH.

    SET PROPERTY OF gs_font 'BOLD' = 1 NO FLUSH.

    SET PROPERTY OF gs_font 'SIZE' = 12.

    CALL METHOD OF h_cell 'INTERIOR' = range.

    SET PROPERTY OF range 'ColorIndex' = 5.

    SET PROPERTY OF range 'Pattern' = 1.
  ENDDO.


ENDFORM.                    " CREATE_SHEET</code>

A to Z of AL11 Operations

$
0
0

With A to Z of OLE Excel in ABAP 7.4 we explored a lot about excel file in SAP. GUI_DOWNLOAD/UPLOAD is also very straight forward. But whenever I get requirement to read multiple files from application server, do processing and store multiple files on another server… well it stressed me hard in the beginning. 🙂

The other day one junior asked me, when we have GUI_DOWNLOAD and UPLOAD, then why do we need to save file in AL11. I had to say one line, so that you can shut down your laptop and go to sleep and when you come in the morning the files would be ready for you to review in AL11. He did not understand my abstract sentence and I am sure, many of you might also be wondering what I wrote.

Question: Why cannot we do GUI_DOWNLOAD/UPLOAD in background mode?

Answer: Jobs can be scheduled in background mode not only at the current time, but it can also be scheduled for future time. If you schedule a background job which does GUI_DOWNLOAD/UPLOAD, your program would dump or cancel out. The reason being, SAP did not want to take chance. GUI_DOWNLOAD/UPLOAD needs your laptop/computer to be switched on for it to save the data in the presentation server path. Say you scheduled a job which has GUI_DOWNLOAD/UPLOD and switched off your laptop and went to bed. When the job actually triggers, where will it save the file since your laptop is switched off? Do you get the catch 22 situation?

Therefore SAP came with Application File Concept (although not just for background job).

Now, let us do the same exercise. You have OPEN DATASET (for AL11) syntax in your program. You schedule a job. Switch off your laptop and go to sleep. In the middle of the night, the job triggers and it saves the file in Application Server (AL11). It is not dependent on your local laptop or desktop. It is at the Server level and Servers normally never sleep (except at planned outage or downtime). 😛

Also Read: Test Driven Development ABAP

Although the File Directory (AL11) concepts are well known to all of us, we always Google for the syntax/tcode/FMs and use them without worrying much about the details and concepts behind the scene (especially lazy programmers like me). 😛

So I thought of doing a favor to myself. Next time I need anything related to T-Code AL11 i.e. SAP Directories, I will not Google. Instead, I will come to this SAPYard page directly where I have listed almost everything we do in AL11. This should be a one stop page for not only all ABAP developers, but also the Basis Team and the Functional/Business team should also find it enriching.

In this article we will try to cover everything about AL11. What AL11 is all about? How to create folder in AL11? Ways to download files from AL11 and upload multiple files into AL11? How to delete files etc. As the title suggests, this is A to Z of all AL11 Operations. 😛

1. What is AL11 and what is it’s use?

Well, AL11 is used for storing files/data on application server so that anyone (SAP) and even third-party systems (Non-SAP) can pick files from it. It is SAP File Directory as it contains directories, multiple folders and files in them. We can upload/download/modify/delete files/folders in t-code AL11.

Below is a quick look of AL11 t-code.

It has two columns:

Name of Directory Parameter: Contains the directory names. It can contain multiple folders and files.

Directory: Contains the whole path of the folders.

2. How to create our own directory in AL11?

This is not supposed to be done by any ABAP developer and usually we will not get any authorization to perform this activity.

Just for sake of a complete document, I coaxed one of my Basis friends in this crime. See I have secret contacts in the SAP Administration and Security Team. My Basis friend was kind enough to provide the below steps. Now, I am wondering what would be the return price I need to pay for this favor. Just kidding. Tell me what you want. Open offer for you. 😉

He said it is no rocket science.

Directories are nothing but folders on Server System paths.

Basis folks and some privileged ones who have the authorization, log into the SAP Server System and create Folders and File. Just like how we create Folders and Files in our Windows/MAC Laptop and PC. As simple as that.

Now suppose we want to create a directory name as ‘ZMM_SOURCE’ on application server, Basis team will login in that Server System and create a folder with the same name which will be our directory.

Below is a screenshot of one such SAP Server System. It looks like normal Windows Folder. Right? But look closely. It is not part of the Windows operating system. It is another Server. If they create a folder in this Server, we can see that same folder in AL11 t-code in SAP.

Now we need to configure this directory in AL11.

There is an option in AL11 called name ‘Configure User Directories

We need to click on that button and need to configure our directory.

After execution, you can see your directory in AL11.

If you double click on this, you will see one file in it as I already created it.

That’s all. These directories are nothing but folders and as easy as right click and create new folder action of Windows. 😊

Also Check – Free Video Course on HANA Models/Views

3. How to create new folder in directory in AL11?

This is also not part of ABAP work. Only Basis teams have authorization to create it. And this is nothing but creating new subfolder under our main folder in application server. Whereas main folder works like directory and subfolder works like folders/subdirectories in AL11.

Whatever locations on server system, where we are creating folders are called as Physical Paths of directories.

If main folder/directory is already configured then there is no need to configure the sub directory again after addition of subfolders. Those would be displayed in AL11 directory path automatically.

4. How to write/upload files on specific folder of AL11?

There are 2 ways to upload the file in AL11:

i) Manually via T-Code CG3Z:

After hitting this T-Code CG3Z, you will get below screen.

Source file on front end: Select your file from your laptop or desktop system and put it in this parameter.

Target file on application server: Provide path of file location in the AL11 directory.

Overwrite file: If it is checked, whatever file name you provided in target file parameter, if it exists in AL11, it will be overwriting with provided file else it will create new file there.

I have provided below details:

And got below message after clicking on the upload button:

Let’s check in AL11 folder now. We can see our file test.xls is created here.

Note: We can ONLY read Doc or Txt files directly by double clicking on the file in AL11 but we can’t read XLS or XLSX files directly. XLS File will give below error.

This error comes because .XLS is a proprietary format of Microsoft. So, unless you have some addons installed on SAP Server, you can’t read XLS files from Application Server.

Do not worry. That is not the end of the world. If you really want to read the XLS file, you can download the file to your local machine (laptop/desktop) and then you can open and read it. 😛

This brings us to question. How to download AL11 files? Please be patient. It will be covered in the later sections in this article.

Here we are still covering how to create Folders/Files in AL11. Let’s look the second way.

ii) By writing ABAP code:

As an ABAP Programmer, usually we do not play with t-codes. We want everything should be done by our code. That gives us more sense of Power. 🙂

Sometimes we get requirement to upload multiple files on application server in a single go with some conditions. Let us look into one practical example:

Suppose I have a requirement to collect data from EKKO and EKPO tables and prepare multiple files for each new PO (EBELN) and store them on AL11 path. Each file should have PO number as file_name.

Sounds interesting yeah… Let’s do this…

First, we will select data from EKKO and EKPO tables and stores them in one internal table.

<code>*** Popoluate tables
SELECT ebeln,
       bukrs,
       bstyp,
       bsart
  INTO TABLE @DATA(gt_ekko)
  FROM ekko
  UP TO 15 ROWS
  WHERE ebeln LIKE '45%'.

IF gt_ekko[] IS NOT INITIAL.

  SELECT ebeln,
         ebelp,
         matnr,
         bukrs
    INTO TABLE @DATA(gt_ekpo)
    FROM ekpo
    FOR ALL ENTRIES IN @gt_ekko
    WHERE ebeln = @gt_ekko-ebeln
    ORDER BY PRIMARY KEY.

ENDIF.</code>

There is a table called FILEPATH.

All folders of an application server are stored in the database table FILEPATH. We can pass our directory/folder name there and can confirm whether the folder exists or not.

<code>CONSTANTS: lc_filename TYPE filepath-pathintern 
                    VALUE 'ZMM_BATCH_DATA_FOR_ASPECT'.

  SELECT SINGLE pathintern
  FROM filepath
  INTO @DATA(lv_logical_path)
  WHERE pathintern = @lc_filename.</code>

As we want to upload multiple files in our folder, we need to call FM FILE_GET_NAME_USING_PATH to get full path of that file and then can open it with Dataset concept:

<code>CALL FUNCTION 'FILE_GET_NAME_USING_PATH'
          EXPORTING
            logical_path               = lv_logical_path
            file_name                  = lv_file_post
          IMPORTING
            file_name_with_path        = lv_filepath
          EXCEPTIONS
            path_not_found             = 1
            missing_parameter          = 2
            operating_system_not_found = 3
            file_system_not_found      = 4
            OTHERS                     = 5.</code>

Now using OPEN DATASET, we can open the file to write fetched data from EKPO into it.

<code>"Open the file to write.
 OPEN DATASET lv_filepath FOR OUTPUT IN TEXT MODE ENCODING DEFAULT.  </code>

Mode ‘Output’ is used for writing data in application server file (all dataset concepts are explained in the 5th point below).

Now using TRANSFER and CLOSE DATASET concept, we can pass all relevant data to corresponding files, save them and take exit from the files.

Whole executable code is provided below:

<code>REPORT zal11_working.

CONSTANTS: lc_e    TYPE c VALUE 'E',
           lc_s    TYPE c VALUE 'S',
           lc_text TYPE string VALUE 'No specific data found for the input',
           lc_msg  TYPE string VALUE 'The batch data is/are stored at the directory successfully',
           lc_filename  TYPE filepath-pathintern VALUE 'ZMM_BATCH_DATA_FOR_ASPECT'.

DATA: lv_filepath TYPE string.

*** Popoluate tables
SELECT ebeln,
       bukrs,
       bstyp,
       bsart
  INTO TABLE @DATA(gt_ekko)
  FROM ekko
  UP TO 15 ROWS
  WHERE ebeln LIKE '45%'.

IF gt_ekko[] IS NOT INITIAL.

  SELECT ebeln,
         ebelp,
         matnr,
         bukrs
    INTO TABLE @DATA(gt_ekpo)
    FROM ekpo
    FOR ALL ENTRIES IN @gt_ekko
    WHERE ebeln = @gt_ekko-ebeln
    ORDER BY PRIMARY KEY.

ENDIF.


IF gt_ekpo[] IS INITIAL.
  MESSAGE lc_text TYPE lc_s DISPLAY LIKE lc_e.
ELSE.

CONSTANTS: lc_filename  TYPE filepath-pathintern VALUE 'ZMM_BATCH_DATA_FOR_ASPECT'.

  SELECT SINGLE pathintern
  FROM filepath
  INTO @DATA(lv_logical_path)
  WHERE pathintern = @lc_filename.

  LOOP AT gt_ekpo ASSIGNING FIELD-SYMBOL(&lt;fs_ekpo>).

    AT NEW ebeln.

      "Logic to create unique file name with each PO number.
      DATA(lv_file_post) = | { &lt;fs_ekpo>-ebeln } .csv|.

      "Remove spaces.
      CONDENSE lv_file_post NO-GAPS.

      CALL FUNCTION 'FILE_GET_NAME_USING_PATH'
          EXPORTING
            logical_path               = lv_logical_path
            file_name                  = lv_file_post
          IMPORTING
            file_name_with_path        = lv_filepath
          EXCEPTIONS
            path_not_found             = 1
            missing_parameter          = 2
            operating_system_not_found = 3
            file_system_not_found      = 4
            OTHERS                     = 5.

      "Open the file to write.
      OPEN DATASET lv_filepath FOR OUTPUT IN TEXT MODE ENCODING DEFAULT.

      "Set intensity of the background color to default.
      FORMAT INTENSIFIED ON.

      "Prepare header of file with comma ',' separated.
      DATA(lv_file) = |EBELN,EBELP,MATNR,BUKRS|.

      "set non-intensified background color.
      FORMAT INTENSIFIED OFF.

      "Transfer header data line by line to file.
      TRANSFER lv_file TO lv_filepath.

      CLEAR lv_file.
    ENDAT.

    "Prepare data of file with comma ',' separated.
    lv_file  = |{ &lt;fs_ekpo>-ebeln } , { &lt;fs_ekpo>-ebelp } , { &lt;fs_ekpo>-matnr } , { &lt;fs_ekpo>-bukrs }|.

    "Remove spaces.
    CONDENSE lv_file.

    "Transfer data line by line to file.
    TRANSFER lv_file TO lv_filepath.

    AT END OF ebeln.
    "CLose the file and save on Application server.
    CLOSE DATASET lv_filepath.
    IF sy-subrc IS INITIAL.
      MESSAGE lc_msg TYPE lc_s.
    ENDIF.
    CLEAR lv_filepath.
    ENDAT.

    CLEAR: lv_file.
  ENDLOOP.
ENDIF.</code>

By using COMMAND-BREAK statements (AT END OF), I have achieved to create new file for new PO. The output is like below:

As shown above, these are different files with different POs. If you open any file, you can get header text line as well as values of cells.

Also Take – End to End Debugging Course for SAP Functionals

5. How to read/download files from AL11?

Above we learned how to write to AL11 file. Now let’s learn how to download them. Here we will discuss 4 different approaches:

i) Download via T-Code CG3Y:

After hitting this tcode, you will see below screen:

  • Source file on application server: Provide the path of file location from AL11 directory which you need to download.
  • Target file on front end: Provide the file name from your system (can be new file) and put it in this parameter.
  • Overwrite file: If it is checked, whatever file name you provided in target file parameter, if it exists in system, it will be overwriting with provided file else it will create new file there.
  • Transfer format for data: Usually ASC format works. Do check your requirement. You might need BIN as well.

As I wanted to save/download test excel file from application server to my desktop, I have provided below details:

It will create test file on the desktop and if it is already present then will overwrite it.

ii) Download using list menu:

If the file is not so long, you can directly download the content from AL11. Navigate to the file. Then Open the file. On System Menu, go to List->Save/Send/Local file:

Question: Why is my AL11 exported file truncated?

Answer: In some case, the exported file is truncated. For List export in AL11, the limit is 512 characters. Apply SAP Note 952766 to increase the limit in AL11.

iii) By using FM ARCHIVFILE_SERVER_TO_CLIENT:

The standard Function module ARCHIVFILE_SERVER_TO_CLIENT can be used for downloading file from AL11. Just provide the PATH: the full path to the file on SAP server and the TARGETPATH: the destination path.

iv) By writing ABAP code:

Now if we have complex requirement like reading/downloading multiple files and do some calculation and massaging of data from the AL11 file then we need to create an ABAP program.

For downloading the file from application server, we must know the below concepts:

a) OPEN DATASET:

<code>OPEN DATASET dset FOR access IN mode [position] 
                                    	 [os_additions] 
                                     	 [error_handling]. </code>

Dset: Contains the file path of AL11.

Access: It has below options –

  • Input: It opens the file for reading. If the file does not exist, the sy-subrc is set to 8.
  • Output: Opens the file for writing. If the file exists, the content will be deleted and replaced. If the file does not exist, the file will be created.
  • Appending: It opens the file for appending. The new content will be added to the end of existing    file or the file will be created if it does not exist. SY-SUBRC = 4 will be triggered if appending is not successful.
  • Update: It opens the file for changes to the existing content.

Mode:  You can specify which mode you want to read the file like binary or text.

ii) TRANSFER DATASET: This statement passes the content of data object dobj to the file specified in dset.

Syntax:

<code>TRANSFER dobj TO dset [LENGTH len] 
                      [NO END OF LINE]. </code>

The file must be open for writes, appends, or changes. If a closed or invalid file is accessed, a handleable exception is raised.

iii) CLOSE DATASET: This statement closes the file specified in dset.

Syntax:

<code>CLOSE DATASET dset.</code>

If the file is already closed or does not exist, the statement is ignored and the return code sy-subrc is set to 0.

An opened file that was not explicitly closed using CLOSE DATASET is automatically closed when the program is exited.

Did I bore you to death with too much of theories?? ufff.. Even I am tired, so I can understand your plight. 🙂

Let’s bring some charm to our learning with the below examples:

In the point 4 above, we created multiple files on our application server now. Let’s read them all. Here I have passed only one file for demonstration. You can make it dynamic as well by passing all file names:

<code>DATA:v_file LIKE v_excel_string VALUE 'E:\Interfaces\ZMM_BATCH_DATA_FOR_ASPECT\4500000001.csv'.         " name of the file

*---  read the file from the application server
OPEN DATASET v_file FOR INPUT IN TEXT MODE ENCODING DEFAULT.</code>

Open the file in READ mode. We can read each line from the file in a loop and split the data based on delimiter to separate the data and store in internal table:

<code>DO.
    READ DATASET v_file INTO wa_tab.
    IF sy-subrc IS INITIAL.
      CLEAR it_string[].
      SPLIT wa_tab AT delimiter INTO TABLE it_string.

      LOOP AT it_string ASSIGNING FIELD-SYMBOL(&lt;fs>).
        ASSIGN COMPONENT sy-tabix OF STRUCTURE wa_tab2 TO FIELD-SYMBOL(&lt;fs_f>).
        IF sy-subrc = 0.
          &lt;fs_f> = &lt;fs>.
        ENDIF.
        AT LAST.
          APPEND wa_tab2 TO it_tab2.
        ENDAT.
      ENDLOOP.

    ELSE.
      EXIT.
    ENDIF.
  ENDDO.  </code>

After receiving the data in an internal table, we can CLOSE the file using CLOSE DATSET syntax:

<code>CLOSE DATASET v_file.</code>

Here I have used ALV factory method to display the results in ALV format. Below is the whole executable program:

<code>REPORT zal11_working.

*--- Type structure declaration.
TYPES: BEGIN OF ty_tab1,
         ebeln TYPE string,
         ebelp TYPE string,
         matnr TYPE string,
         bukrs TYPE string,
       END OF ty_tab1.

*--- work area and internal table declaration.
DATA : it_tab TYPE STANDARD TABLE OF alsmex_tabline,
       wa_tab TYPE alsmex_tabline,
       it_tab2 TYPE STANDARD TABLE OF ty_tab1,
       wa_tab2 TYPE ty_tab1,
       it_string TYPE TABLE OF string.

*--- variable  declaration
DATA: v_excel_string(2000) TYPE c,
      v_file               LIKE v_excel_string VALUE 'E:\Interfaces\ZMM_BATCH_DATA_FOR_ASPECT\4500000001.csv',         " name of the file
      delimiter            TYPE c VALUE ',',
      t_field(1000)        TYPE c OCCURS 0.                                                        " delimiter with default value space

*--- Factory method objects
DATA: lr_table TYPE REF TO cl_salv_table,
      lr_columns type ref to cl_salv_columns_table,
      lr_column  type ref to cl_salv_column_table.

*---  read the file from the application server
OPEN DATASET v_file FOR INPUT IN TEXT MODE ENCODING DEFAULT.
IF sy-subrc NE 0.
  WRITE:/ 'error opening file'.
ELSE.
  DO.
    READ DATASET v_file INTO wa_tab.
    IF sy-subrc IS INITIAL.
      CLEAR it_string[].
      SPLIT wa_tab AT delimiter INTO TABLE it_string.

      LOOP AT it_string ASSIGNING FIELD-SYMBOL(&lt;fs>).
        ASSIGN COMPONENT sy-tabix OF STRUCTURE wa_tab2 TO FIELD-SYMBOL(&lt;fs_f>).
        IF sy-subrc = 0.
          &lt;fs_f> = &lt;fs>.
        ENDIF.
        AT LAST.
          APPEND wa_tab2 TO it_tab2.
        ENDAT.
      ENDLOOP.

    ELSE.
      EXIT.
    ENDIF.
  ENDDO.
ENDIF.

CLOSE DATASET v_file.

*------display the data from the internal table

IF it_tab2[] IS NOT INITIAL.
   DELETE it_tab2 INDEX 1. "Delete header line.
ENDIF.

cl_salv_table=>factory(
               IMPORTING r_salv_table = lr_table
               CHANGING t_table = it_tab2 ).

lr_columns = lr_table->get_columns( ).

  try.
      lr_column ?= lr_columns->get_column( 'EBELN' ).
      lr_column->set_short_text( 'EBELN' ).

      lr_column ?= lr_columns->get_column( 'EBELP' ).
      lr_column->set_short_text( 'EBELP' ).

      lr_column ?= lr_columns->get_column( 'MATNR' ).
      lr_column->set_short_text( 'MATNR' ).

      lr_column ?= lr_columns->get_column( 'BUKRS' ).
      lr_column->set_short_text( 'BUKRS' ).

    catch cx_salv_not_found.                            "#EC NO_HANDLER
  endtry.

lr_table->display( ).</code>

After its execution, you will get below screen:

Also Read: How I created my First OData Service in SAP

6. How to delete files from AL11?

After all you would not want to keep the test files in the application server after testing. So, to delete all unwanted files, you can use below FM: EPS_DELETE_FILE

It has below structures in it:

Enter the File name in the FILE_NAME and the directory path (Excluding the file name) in the DIR_NAME and execute.

After execution, the file will disappear permanently from the AL11 location. As Spiderman’s uncle said, “With more Power comes more Responsibility“. So please use it judiciously.

This FM we can use at program level as well if required to delete files in mass.

We can use DELETE DATASET statement too at program level to delete files from application server.

Syntax:

<code>DELETE DATASET dset.</code>

7. Standard SAP Report to upload and download files?

Yes, you read it right. SAP has already provided a report which is used for uploading file from presentation server to application server and vice versa. Awesome yeah ✌

Report name: CACS_FILE_COPY

When you execute this report, you will get below selection screen.

Now I wanted to store a file on application server from my desktop:

After successful execution, we can get below pop-up message:

Now let’s check in AL11:

Ohh yeah!!.. So easy and got the file too in AL11.

8. Get all files from the directory/folder of application server?

If you want to know what files are present in any AL11 folder, then you can use below 2 FMs:

EPS_GET_DIRECTORY_LISTING or EPS2_GET_DIRECTORY_LISTING

Let’s see how it works.

Here, we have to provide directory/folder path to get list of files present in it.

As you see in above snapshot, we got 62 files of our directory. If you check the DIR_LIST table, you will get below details:

This functionality you can use in the program whenever you want to do mass processing like read file/download file/delete file etc from single directory. It indeed saves time and good for performance.

Well, what else you want to do with AL11? I am blank now. Hell tired. Honestly, I don’t think you will need to search any other thread in future for any queries related to AL11. This page should be mother of all pages for AL11 and one source of truth for all AL11 requirements.

If I learn something new about AL11, I will come back and update this page.

Do you have any trick or tip to share for AL11? Feel free to email us at mail@sapyard.com or mailsapyard@gmail.com. Or you may also leave your findings in the comment section below.

Sharing is Caring. Would you mind sharing the link so that it reaches maximum audience? And our SAP Fraternity can benefit from our work.

If you want to have real time discussions and resolutions, do join our SAP Technical Telegram Group where we have more than 4885+ active SAP consultants from 6 Continents.

Please Note: You need to install Telegram App on your mobile first and then you can join the group using the above link.

Please SUBSCRIBE to SAPYard’s Youtube Channel for Free End to End SAP Video Course and Training.

Free SAP ABAP for HANA Training Tutorials Exercises.

ABAP – Power To Kill

$
0
0

OK. The title might look like click bait, but ABAP developers do have the Power to Kill the SAP Session. 😛

This document has provided me the opportunity to flaunt the power of an ABAP team members. You have always undermined the service of an ABAPer. You have used them for SAP house cleaning tasks. But ABAP team members are the backbone of any SAP Project. So never underestimate the powerful ABAPer. 😛

Remember my last article A to Z of AL11? A basis colleague of mine helped me know about the server details/creation of directory etc. Now it was payback time. I was a little tensed, what I might need to offer for the help but my colleague is a simple guy. He just wanted a help with an annoying daily job nothing else. Fewww!! 😉

Now was the time to thank him with my ABAP skill.

Over a tea break, he casually told me that the Basis team has one job scheduled to update some master data. They schedule the job during the lean window time frame after business hours but somehow very often they find some users locking the transaction the basis team needs to update. And if any user is sitting in that material master, the basis job failed and the basis team had to re-run again.

Our Basis Team members were frustrated as they had to monitor the job every day and reschedule it again if it failed.

After hearing his pain, I could empathize with his plight. After all ABAPers are also victim of similar agony. Anyway, I had to return the favor and this was the right time.

Here is the summary of the issue:

There is a batch job which updates the material master (for simplicity assume the data in table MARA) on daily basis. But if some user has logged into that material in MM02 then the job fails to update the material master and logs the lock object error. Basis team has to re-run the job again.

How many times have you opened the SM30 t-code or MM02 or VA02 sessions in the evening and left for home without signing off your system? Now anyone working late night or in another time zone cannot use SM30 to maintain the table you have opened. Next time you are done for the day, log off your SAP system and shut down your machine. It will help global warming and keep the earth green.  (from Gyan Bhandar) 😛

My Basis friend wanted a solution on priority basis.

High level Solution Architecture

  • Check how many users are locking the Material Master transaction.
  • Provide Pop Up warning message to those users to save their session and come out of transaction within 5 minutes.
  • After 5 minutes (time should be configurable and not hard coded), check again for those are still locking the transaction. Provide the 2nd and final warning to save their work within 5 minutes.
  • After further 5 minutes, the material master transaction which the user is locking would be released/closed. All other transactions and sessions which are not interfering with the job is left untouched.

Also Read: Simple SAP Security Breach

Sounds interesting? Now, action time.

Steps:

Identify Users:

Function module ENQUEUE_READ provides the details of locks.

Here, I am going to provide sy-uname = my name as I am going to test this object and don’t want to close other’s sessions.

After full testing, will remove my name with ‘*’ to get all the users.

*** FM to get all lock details of MARA table (like SM12)

<code> CALL FUNCTION 'ENQUEUE_READ'
   EXPORTING
     gname                 = 'MARA'
     guname                = '$SBHANDARE'
   TABLES
     enq                   = gt_lock_details
   EXCEPTIONS
     communication_failure = 1
     system_failure        = 2
     OTHERS                = 3.
 IF sy-subrc &lt;> 0.
   EXIT.
 ENDIF.  </code>

Get active sessions:

Once we know which users are locking our transaction, we need to find out how many sessions are open in the users’ system.

There is a standard ABAP class SERVER_INFO which will give us all active session info.

<code>*** Get all user sessions
 CREATE OBJECT server_info.
 gt_session_list = server_info->get_session_list( with_application_info = 1 ).</code>

Now we need to filter out the sessions which belong to our users with client details.

<code>*** Filter user sessions on the basis of username and client.
 gt_session_list_user = VALUE #( FOR ls_lock_details IN gt_lock_details
                                 FOR ls_session_list IN gt_session_list
                                    WHERE ( user_name = ls_lock_details-guname
                                           AND tenant = sy-mandt )
                                             ( ls_session_list ) ).</code>

By now, we have all open sessions of our target users only.

Time to send warnings and notifications to these stubborn users. May be hard working users. 😛

We use FM ‘POPUP_TO_CONFIRM’ for pop up messages. But there is a catch. POPUP_TO_CONFRIM will give message to the person who is running the report or program. The user who has opened the screen in change mode will not get notification if we use this FM. So, what is the alternative?

Also Read: Can you really restrict developers from executing any t-code?

Chat in SAP. Yes, you read it right. You can send anonymous pop up messages to anyone in your office SAP network. The good thing is they will not be able to find the sender name easily. I hope you are not thinking what I am thinking. Ready for teenage prank? 😀

FM ‘TH_POPUP’ helps us to send pop up messages to any user in the network system. Works perfectly in our scenario.

<code>DATA(gv_msg) = |You are locking Transaction { gv_tcode }. Please save and leave the transaction within 5 Secs!!!|. 

DATA gv_message TYPE sm04dic-popupmsg.  

gv_message = gv_msg.
 
     LOOP AT gt_lock_details INTO gs_lock_details.
 
       CALL FUNCTION 'TH_POPUP'
         EXPORTING
           client         = sy-mandt
           user           = gs_lock_details-guname
           message        = gv_message
         EXCEPTIONS
           user_not_found = 1
           OTHERS         = 2.
 
     ENDLOOP.</code>

Looping through the users table, we can send notification to all the users who are locking our transaction.

Let’s wait from 5 minutes so that users can save the data and close the session.

<code>WAIT UP TO 300 SECONDS.</code>

ABAP Wait statement in SAP Workflow nearly brought my SAP Production system to halt. That is an interesting story which I will tell some other day. I even have the title of the article in mind – “Wait Wait.. do NOT use me”. 😛

After 5 minutes, check again for the list of users who are sitting on our transaction. In between if any new user locks the transaction; this poor guy would only get 5 minutes. They would not get the 2nd warning as the first list of users are getting.

<code>gv_msg = |grrr..You are still locking Transaction { gv_tcode }. Your session will be killed soon!!!|.
     gv_message = gv_msg. 
 
     LOOP AT gt_lock_details ASSIGNING &lt;fs_lock_details>.
 
       CALL FUNCTION 'TH_POPUP'
         EXPORTING
           client         = sy-mandt
           user           = &lt;fs_lock_details>-guname
           message        = gv_message
         EXCEPTIONS
           user_not_found = 1
           OTHERS         = 2.
 
     ENDLOOP.</code>

Wait from final 5 minutes to give the users one more chance to save the data and close the session.

<code>WAIT UP TO 300 SECONDS.</code>

After the 10th minute, take the list of users who are still locking the transaction. They are the guys who have already left for the day or are talking on phone for more than 10 minutes. By the way, with whom would they talk at these late hours? 😛

Action Time.

We will use system kernel call to get all active session details same as t-code SM04.

<code>        "get technical info for the user
         CALL 'ThUsrInfo' ID 'OPCODE' FIELD opcode_usr_info
           ID 'TID' FIELD &lt;fs_session_list>-logon_hdl
           ID 'WITH_APPL_INFO' FIELD with_appl_info
           ID 'TABLE' FIELD gt_usr_info[].</code>

Here we will get a huge list of multiple rows for each session number (0-6) that the user has opened. Each login by a user is assigned a unique login ID which is generated based on parameters you log in with viz: the client number, system, and language. This session ID has the form of TXX_UXXXXX, where X can be any number 0-9.

Each session then has a session ID which is comprised of first the login ID as described above, with an appended _MX, where X in this case is the session number, 0-6 (session 1-7). So, we are interested in the .session value of this technical list:

modelinfo[0] is for session 1. Similarly [1] and [2] would be for session 2 and 3 respectively.

modeinfo[1].session = T51_U11407_M1

Session 3 would have: modeinfo[2].session = T51_U11407_M2

Now we need to use the ‘/UPD’ value in the technical info to get the exact session used by the user. You guessed it right. UPD must be for Update Mode.

<code>CONCATENATE &lt;fs_lock_details>-gusrvb '/UPD' INTO DATA(gv_value).
 
 READ TABLE gt_usr_info ASSIGNING FIELD-SYMBOL(&lt;fs_usr_info>)
                        WITH KEY value = gv_value.
 IF sy-subrc IS INITIAL.
 "The key for the value is 'modeinfo[X].enq_info'.... we need just the X
    gv_modus_index = &lt;fs_usr_info>-field+9(1).
 ELSE.
    CONTINUE.
 ENDIF.</code>

Now we will do above process and make the list of the users with required sessions which we need to close/kill.

After preparing the final user list, I tried to figure out the right way to kill the session of user but failed if initial attempt.

I tried to do the BDC recording of SM12 with delete functionality but that didn’t work. Not sure, may be I did not have the right authorization. I did not dig deep though.

Also Read: How to Delete Foreign Lock Entries in Debug

Then I researched t-code SM04. I did the BDC recording of it and sent user session number to kill that session.

<code>" call transaction SM04, sync (S), no screens
         CALL TRANSACTION 'SM04'
                 USING bdcdata
                 MODE 'N'
                 UPDATE 'S'.
 
         " Now use bdc to end the session with that session id
         CLEAR bdcdata[].
         PERFORM bdcdaten USING:
           'X' 'SAPMSSY0' '0120',
             ' ' 'BDC_CURSOR' '04/03',
             ' ' 'BDC_OKCODE' '=&amp;OL0'.
         PERFORM bdcdaten USING:
           'X' 'SAPLSKBH' '0800',
             ' ' 'BDC_CURSOR' 'GT_FIELD_LIST-SELTEXT(01)',
             ' ' 'BDC_OKCODE' '=WLSE',
             ' ' 'GT_FIELD_LIST-MARK(01)' 'X',
             ' ' 'BDC_SUBSCR' 'SAPLSKBH          0810SUB810'.
         PERFORM bdcdaten USING:
           'X' 'SAPLSKBH' '0800',
             ' ' 'BDC_CURSOR' 'GT_FIELD_LIST-SELTEXT(01)',
             ' ' 'BDC_OKCODE' '=CONT',
             ' ' 'BDC_SUBSCR' 'SAPLSKBH          0810SUB810'.
         PERFORM bdcdaten USING:
           'X' 'SAPMSSY0' '0120',
             ' ' 'BDC_CURSOR' '04/03',
             ' ' 'BDC_OKCODE' '=PS 63'.
         PERFORM bdcdaten USING:
           'X' 'SAPMSSY0' '0120',
             ' ' 'BDC_CURSOR' '02/76',
             ' ' 'BDC_OKCODE' '=&amp;IC1'.
         PERFORM bdcdaten USING:
           'X' 'SAPMSSY0' '0120',
             ' ' 'BDC_CURSOR' '02/76',
             ' ' 'BDC_OKCODE' '=&amp;ILT'.
         PERFORM bdcdaten USING:
           'X' 'SAPLSSEL' '1104',
             ' ' 'BDC_OKCODE' '=CRET',
             ' ' 'BDC_SUBSCR' 'SAPLSSEL           1105%_SUBSCREEN_FREESEL',
             ' ' 'BDC_CURSOR' '%%DYN001-LOW',
             ' ' '%%DYN001-LOW' gv_ssi_session_id.
         PERFORM bdcdaten USING:
           'X' 'SAPMSSY0' '0120',
             ' ' 'BDC_CURSOR' '04/15',
             ' ' 'BDC_OKCODE' '=&amp;IC1'.
         PERFORM bdcdaten USING:
           'X' 'RSM04000_ALV_NEW' '2000',
             ' ' 'BDC_CURSOR' gv_modus_string,
             ' ' 'BDC_OKCODE' '=DEL'.
         PERFORM bdcdaten USING:
           'X' 'SAPMSSY0' '0120',
             ' ' 'BDC_CURSOR' '04/15',
             ' ' 'BDC_OKCODE' '=&amp;F15'.
 
         " call transaction SM04, sync (S), no screens
         CALL TRANSACTION 'SM04'
                 USING bdcdata
                 MODE 'P'
                 UPDATE 'N'. </code>

Finally let’s go in for the kill and shut down that session(s). Once we do that, we log which session/user/transaction combo we deleted to the spool so that no one can cry foul play later. We gave enough time people!

Don’t feel sorry for closing the sessions. We warned the users twice, gave them 10 minutes to save the thing but if they are still blocking our transaction. We were forced for the attack.

Sometimes, we need to be difficult. Kicking the users out of the system ensures that the overall system is synced with up to date master data.

This is my favorite line from Spiderman “With great power comes great responsibility”. Always use your super power for the good reason. 🙂

Check this video for the output in action:

You can use the below code in your system and execute it. Do not forget to remove the hard code 300 seconds and my sy-uname. If your system is below 7.4, you need to change the new syntax to old and also you might not have some data elements.

Working Code

<code>*&amp;---------------------------------------------------------------------*
 *&amp; Report ZUSER_SESSION_KILL
 *&amp;---------------------------------------------------------------------*
 *&amp;
 *&amp;---------------------------------------------------------------------*
 REPORT zuser_session_kill.
 
 *** Types declarations.
 DATA:BEGIN OF gt_usr_info OCCURS 10,
        field(40),
        value(80),
      END OF gt_usr_info.
 
 *** Variable declarations.
 DATA: gv_time             TYPE string,
       gv_tcode            TYPE eqetcode,
       gv_ssi_session_id   TYPE ssi_session_id,
       gv_modus_num_found  TYPE ssi_session_hdl,
       gv_modus_index_char TYPE numc1,
       gv_message          TYPE sm04dic-popupmsg,
       gv_modus_string     TYPE string,
       gv_modus_index      TYPE string,
       gv_stop             TYPE c,
       th_opcode(1)        TYPE x.
 
 *** Internal table declarations.
 DATA: gt_lock_details      TYPE TABLE OF seqg3,
       gt_session_list      TYPE TABLE OF ssi_session_info,
       gt_session_list_user TYPE TABLE OF ssi_session_info,
       gt_sessions_user     TYPE TABLE OF ssi_session_info.
 
 *** Workareas declarations.
 DATA: server_info     TYPE REF TO cl_server_info,
       bdcdata         LIKE bdcdata OCCURS 0 WITH HEADER LINE.
 
 *** Constants declarations.
 CONSTANTS: opcode_usr_info LIKE th_opcode VALUE 52,
            with_appl_info  TYPE ssi_bool VALUE 1. " this is the internal SAP number
 
 
 CLEAR gv_time.
 
 "To stop the loop processing.
 WHILE gv_stop NE abap_true.
 
   "send notification to users, those who are locking our table, do it till 10 seconds.
   IF gv_time IS INITIAL. " First warning message after 5 minutes
 *** fm to GET all lock details of mara table (like sm12)
     CALL FUNCTION 'ENQUEUE_READ'
      EXPORTING
        gname                 = 'MARA'
        guname                = '$SBHANDARE'
      TABLES
        enq                   = gt_lock_details
      EXCEPTIONS
        communication_failure = 1
        system_failure        = 2
        OTHERS                = 3.
        IF sy-subrc &lt;> 0.
          EXIT.
        ENDIF.
 
 
     DATA(gv_msg) = |You are locking Transaction { gv_tcode }. Please save and leave the transaction within 5 Secs!!!|.
     gv_message = gv_msg.
 
 
     LOOP AT gt_lock_details ASSIGNING FIELD-SYMBOL(&lt;fs_lock_details>).
 
       CALL FUNCTION 'TH_POPUP'
         EXPORTING
           client         = sy-mandt
           user           = &lt;fs_lock_details>-guname
           message        = gv_message
         EXCEPTIONS
           user_not_found = 1
           OTHERS         = 2.
 
     ENDLOOP.
 
     WAIT UP TO 300 SECONDS.
     gv_time = '01'.
 
     FREE: gt_lock_details[], gv_message, gv_msg.
     "send notification to users, those who are locking our table after first notification too, do it from 5 seconds to 45 seconds.
   ELSEIF gv_time = '01'. " Second warning message
 *** FM to get all lock details of MARA table (like SM12)
     CALL FUNCTION 'ENQUEUE_READ'
       EXPORTING
         gname                 = 'MARA'
         guname                = '$SBHANDARE'
       TABLES
         enq                   = gt_lock_details
       EXCEPTIONS
         communication_failure = 1
         system_failure        = 2
         OTHERS                = 3.
     IF sy-subrc &lt;> 0.
       EXIT.
     ENDIF.
 
 
     gv_msg = |Uff...You are still locking Transaction { gv_tcode }. Your session will be killed soon!!!|.
     gv_message = gv_msg.
 
 
     LOOP AT gt_lock_details ASSIGNING &lt;fs_lock_details>.
 
       CALL FUNCTION 'TH_POPUP'
         EXPORTING
           client         = sy-mandt
           user           = &lt;fs_lock_details>-guname
           message        = gv_message
         EXCEPTIONS
           user_not_found = 1
           OTHERS         = 2.
 
     ENDLOOP.
 
     WAIT UP TO 300 SECONDS.
     gv_time = '03'.
 
     FREE: gt_lock_details[], gv_message, gv_msg.
 
     "kill the user sessions after 50 seconds.
   ELSEIF gv_time = '03'.
     gv_stop = abap_true.
 
 *** FM to get all lock details of MARA table (like SM12)
     CALL FUNCTION 'ENQUEUE_READ'
       EXPORTING
         gname                 = 'MARA'
         guname                = '$SBHANDARE'
       TABLES
         enq                   = gt_lock_details
       EXCEPTIONS
         communication_failure = 1
         system_failure        = 2
         OTHERS                = 3.
     IF sy-subrc &lt;> 0.
       EXIT.
     ENDIF.
 
 *** Get all user sessions
     CREATE OBJECT server_info.
     gt_session_list = server_info->get_session_list( with_application_info = 1 ).
 
 *** Filter user sessions on the basis of username and client.
     gt_session_list_user = VALUE #( FOR ls_lock_details IN gt_lock_details
                                           FOR ls_session_list IN gt_session_list
                                           WHERE ( user_name = ls_lock_details-guname
                                               AND tenant = sy-mandt )
                                                 ( ls_session_list ) ).
 
     FREE: gt_session_list[].
 
     LOOP AT gt_lock_details ASSIGNING &lt;fs_lock_details>.
       gv_tcode = &lt;fs_lock_details>-gtcode.
 
       CLEAR gt_sessions_user.
       LOOP AT gt_session_list_user ASSIGNING FIELD-SYMBOL(&lt;fs_session_list>). " loop at all open sessions to build a list of just the user sessions
 
         CLEAR gv_modus_index.
         CLEAR gt_usr_info[].
 
         "get technical info for the user
         CALL 'ThUsrInfo' ID 'OPCODE' FIELD opcode_usr_info
           ID 'TID' FIELD &lt;fs_session_list>-logon_hdl
           ID 'WITH_APPL_INFO' FIELD with_appl_info
           ID 'TABLE' FIELD gt_usr_info[].
 
         "Need to use the '/UPD' value in the technical info to get the exact session used by user
         CONCATENATE &lt;fs_lock_details>-gusrvb '/UPD' INTO DATA(gv_value).
 
         READ TABLE gt_usr_info ASSIGNING FIELD-SYMBOL(&lt;fs_usr_info>)
                    WITH KEY value = gv_value.
         IF sy-subrc IS INITIAL.
           "The key for the value is 'modeinfo[X].enq_info'.... we need just the X
           gv_modus_index = &lt;fs_usr_info>-field+9(1).
         ELSE.
           CONTINUE.
         ENDIF.
 
         " If we found a session with that number, we know we had the correct logon... we can add this to the user list
         IF gv_modus_index IS NOT INITIAL.
           " convert to type ssi_session_handle for later lookup
           gv_modus_num_found = gv_modus_index.
           " get the session id for this specific session.
           CONCATENATE 'modeinfo[' gv_modus_index '].session' INTO DATA(gv_field). " this time we need to find the value from the field
 
           READ TABLE gt_usr_info ASSIGNING &lt;fs_usr_info> WITH KEY field = gv_field.
           IF sy-subrc IS INITIAL.
             gv_ssi_session_id = &lt;fs_usr_info>-value.
           ENDIF.
 
           APPEND &lt;fs_session_list> TO gt_sessions_user.
         ENDIF.
       ENDLOOP.
 
       IF gt_sessions_user[] IS NOT INITIAL.
         SORT gt_sessions_user BY session_hdl ASCENDING.
 
         READ TABLE gt_sessions_user TRANSPORTING NO FIELDS
                    WITH KEY session_hdl = gv_modus_num_found .
         IF sy-subrc IS INITIAL.
           " Kill only the session from the table that matches the index of the modus found
           gv_modus_index_char = sy-tabix.
           CLEAR gv_modus_string.
           CONCATENATE 'MODUS-MTCODE(0' gv_modus_index_char ')' INTO gv_modus_string.
         ENDIF.
 
         IF gv_modus_index_char EQ '0'.
           CONTINUE.
         ENDIF.
 
         " First ensure that sm04 is called and set to the sessions view
         CLEAR bdcdata[].
         PERFORM bdcdaten USING:
           'X' 'SAPMSSY0' '0120',
             ' ' 'BDC_CURSOR' '04/03',
             ' ' 'BDC_OKCODE' '=SESSIONS'.
 
         " call transaction SM04, sync (S), no screens
         CALL TRANSACTION 'SM04'
                 USING bdcdata
                 MODE 'N'
                 UPDATE 'S'.
 
         " Now use bdc to end the session with that session id
         CLEAR bdcdata[].
         PERFORM bdcdaten USING:
           'X' 'SAPMSSY0' '0120',
             ' ' 'BDC_CURSOR' '04/03',
             ' ' 'BDC_OKCODE' '=&amp;OL0'.
         PERFORM bdcdaten USING:
           'X' 'SAPLSKBH' '0800',
             ' ' 'BDC_CURSOR' 'GT_FIELD_LIST-SELTEXT(01)',
             ' ' 'BDC_OKCODE' '=WLSE',
             ' ' 'GT_FIELD_LIST-MARK(01)' 'X',
             ' ' 'BDC_SUBSCR' 'SAPLSKBH                                0810SUB810'.
         PERFORM bdcdaten USING:
           'X' 'SAPLSKBH' '0800',
             ' ' 'BDC_CURSOR' 'GT_FIELD_LIST-SELTEXT(01)',
             ' ' 'BDC_OKCODE' '=CONT',
             ' ' 'BDC_SUBSCR' 'SAPLSKBH                                0810SUB810'.
         PERFORM bdcdaten USING:
           'X' 'SAPMSSY0' '0120',
             ' ' 'BDC_CURSOR' '04/03',
             ' ' 'BDC_OKCODE' '=PS 63'.
         PERFORM bdcdaten USING:
           'X' 'SAPMSSY0' '0120',
             ' ' 'BDC_CURSOR' '02/76',
             ' ' 'BDC_OKCODE' '=&amp;IC1'.
         PERFORM bdcdaten USING:
           'X' 'SAPMSSY0' '0120',
             ' ' 'BDC_CURSOR' '02/76',
             ' ' 'BDC_OKCODE' '=&amp;ILT'.
         PERFORM bdcdaten USING:
           'X' 'SAPLSSEL' '1104',
             ' ' 'BDC_OKCODE' '=CRET',
             ' ' 'BDC_SUBSCR' 'SAPLSSEL                                1105%_SUBSCREEN_FREESEL',
             ' ' 'BDC_CURSOR' '%%DYN001-LOW',
             ' ' '%%DYN001-LOW' gv_ssi_session_id.
         PERFORM bdcdaten USING:
           'X' 'SAPMSSY0' '0120',
             ' ' 'BDC_CURSOR' '04/15',
             ' ' 'BDC_OKCODE' '=&amp;IC1'.
         PERFORM bdcdaten USING:
           'X' 'RSM04000_ALV_NEW' '2000',
             ' ' 'BDC_CURSOR' gv_modus_string,
             ' ' 'BDC_OKCODE' '=DEL'.
         PERFORM bdcdaten USING:
           'X' 'SAPMSSY0' '0120',
             ' ' 'BDC_CURSOR' '04/15',
             ' ' 'BDC_OKCODE' '=&amp;F15'.
 
         " call transaction SM04, sync (S), no screens
         CALL TRANSACTION 'SM04'
                 USING bdcdata
                 MODE 'P'
                 UPDATE 'N'.
 
         " proper logging. times, order number, and transaction added as well as
         WRITE: / 'User session from user ', &lt;fs_lock_details>-guname, 'with session number', gv_modus_index_char, 'was deleted.'.
         WRITE: / 'Object Lock Time Length:', &lt;fs_lock_details>-gttime.
 
       ENDIF.
     ENDLOOP.
   ENDIF.
 ENDWHILE.
 
 
 *&amp;---------------------------------------------------------------------*
 *&amp;      Form  BDCDATEN
 *&amp;---------------------------------------------------------------------*
 FORM bdcdaten USING start fnam fval.
 
   CLEAR bdcdata.
   IF start = abap_true.
     bdcdata-dynbegin = start.
     bdcdata-program  = fnam.
     bdcdata-dynpro   = fval.
   ELSE.
     bdcdata-fnam = fnam.
     bdcdata-fval = fval.
   ENDIF.
   APPEND bdcdata.
 
 ENDFORM.                               " BDCDATEN</code>

Below are the output snapshots of the code:

First Warning message to user (done for 5 secs time as doing testing):

Second Warning message after 10 minutes to user:

SM12 entry of lock:

After 10 minutes, the sessions which are locking the material master will be closed.

Hope you found this interesting. Try it internally in your team and remember its impact. Also take note of the forbidden system kernel calls.

Your comments make me smile. Please comment!

We have a very active Telegram (App) SAP Technical Group with more than 4930+ SAP Technical Practitioners from 6 Continents of the SAP World. Please join it using below link.
Telegram SAP Technical Discuss Group. You need to install the Telegram App first on your mobile device. Once you have it on your mobile, you can join the group and also access it from the Web on your computer and laptop.

Please SUBSCRIBE to SAPYard’s Youtube Channel for Free End to End SAP Video Course and Training.

Check HANA-ABAP Tutorials

CDS Part 17. How to Overcome GUID Mismatch Linking Problem in ABAP CDS?

$
0
0

In my last article, I tried to show the limitation of Open SQL with respect to SQL Script. Today, I would like to point out the limitation in CDS and a simple tweak to overcome it. ABAP CDS Views have a problem in that you cannot join tables together with mismatched GUIDs, that is to say where one GUID is defined as RAW and the other is defined as CHAR.

BUT000 has PARTNER_GUID defined as RAW(16) and CRMD_PARTNER has PARTNER_NO defined as CHAR(32).  When you try to join these two tables together in an ABAP CDS View, it will give an error and the view will not activate due to the join fields being mismatched.  This is a restriction imposed by HANA SQL query language used for ABAP CDS Views.

We have taken GUID for our demonstration. The issue can be with any field with data type mismatch. And our tweak would be applicable for all.

The simple solution to bypass this issue is to create another TYPE of View to handle the join.  The type of view I am referring to is an ABAP Dictionary View. Yes, normal ABAP Dictionary View can come to the rescue of the CDS View. 🙂

Unlike ABAP CDS views, Dictionary Views use Native SQL, which allows joining of mismatched data types for GUIDs.

Dictionary views are created within the SAP GUI editor (ABAP Perspective of HANA Studio or in normal GUI), require no source code entries, so no DDL source file is associated with them.

Also TakeEnd to End Video Course on How to Consume HANA Models in ABAP?

Some limitations of dictionary views:

  1. Only tables can be joined.
  2. They can only be joined using INNER JOIN.
  3. You cannot alias table names, so each table can only be used once in the view.
  4. No calculations or grouping is possible.

As you can tell from the above limitations, these views need to be kept basic, and this is fine for the purpose we have, which is simply to create a linking view between two tables which can then be combined with other more complex ABAP CDS views.

Dictionary views are created in SAP HANA Development Studio under the ABAP perspective or directly in the SAP GUI using transaction code SE11.

If creating in SAP HANA Development Studio, follow the steps below:

  1. Go to ABAP perspective
  2. Open the package to which the view is to be added
  3. Open the Dictionary >> Views folder.
  4. Right click on the “Views” folder name
  5. Select New Dictionary View

Or alternatively

  1. Go to ABAP perspective
  2. Right click on the package to add the view to
  3. Click New and select “Other ABAP repository object”
  4. Open the Dictionary folder
  5. Select Dictionary view and Next

Next

6. The following screen will be displayed.

There are two types of view you can create; Dictionary View and External ViewIn our case we are only interested in Dictionary Views.

Also TakeEnd to End Video Course on ABAP 7.4 New Syntax & Features

7. Enter the view name desired and select Dictionary View

8. The maintenance screen for transaction code SE11 is displayed, under which you can then select appropriate tabs to add tables, join criteria, the fields to output and the selection criteria to use.

Alternatively, the view can be created directly within the SAP GUI using the following steps.

  1. Enter transaction code SE11
  2. Enter the view name and click create

3. Enter a description, then define the tables and join conditions required.

4. Enter the basic fields required, for a link table these should simply be the CLIENT and the two GUIDs used in the join. 

You can add further fields if desired but bear in mind, this view is primarily being created as a linking table, so keeping it simple is best.

5. Once all entries are made, save, activate and assign to a transport.

6. The activation log will give the following warnings, showing the type mismatch between the GUIDs.

We can ignore the warning. Our database view is ready.

Also Read: End to End SAP ABAP on HANA Tutorials.

7. Now we have a view that can be used for joining CRMD_PARTNER with BUT000 within and ABAP CDS view as follows:

<code>SELECT FROM CRMD_PARTNER AS PART
INNER JOIN ZVW_PARTNER_LINK AS LINK
ON LINK.CLIENT = PART.CLIENT 
AND LINK.PARTNER_NO = PART.PARTNER_NO
INNER JOIN BUT000 AS B000
ON B000.CLIENT = LINK.CLIENT
AND B000.PARTNER_GUID = LINK.PARTNER_GUID
{
&lt;desired fields>
}</code>

Look at the database view closely in the above snippet. The database view has become the connecting link between the two CDS Views and has effectively overcome the problem of joining CRMD_PARTNER direct with BUT000 with GUID mismatch (data element mismatch). And because it is used in a CDS View, this enables use of LEFT OUTER JOIN between the tables if needed.

This is simple trick which we have used in our real project. There can be better way which I am not aware of. If you know of a better solution, please put it in the comments section. Together we can learn better and provide optimized solutions to our clients.

In my next article, I would share the different reporting in SAP viz BI Reporting, Operational Reporting and reporting tools like KPI modeller, Crystal Reports, Lumina etc. So, please stay tuned.

We have a very active Telegram (App) SAP Technical Group with more than 5000+ SAP Technical Practitioners from 6 Continents of the SAP World. Please join it using below link.
Telegram SAP Technical Discuss Group. You need to install the Telegram App first on your mobile device. Once you have it on your mobile, you can join the group and also access it from the Web on your computer and laptop.

Please SUBSCRIBE to SAPYard’s Youtube Channel for Free End to End SAP Video Course and Training.

Free Step by Step Core Data Services Exercises

SORTing Algorithm – Interview Question

$
0
0

In our SAP Technical Telegram Group, we discuss everything about SAP. UI5, OData, Fiori issue, New ABAP syntax, CDS approaches, Performance Tuning debates. You think of a topic, we discuss it in our group. No wonder, we are one of the largest SAP Technical group with zero spamming.

Yesterday, one member asked the below question. It was no doubt an interview question from one of the largest IT Company in the world.

Question: How to write the entries of first and second table to third table without SORT statement?

No SORT statement should be there in the solution.

The moment this question was floated, there was a debate among the members asking what is the business requirement? Why can we not use SORT?

Then the member who asked this question revealed it is one of the interview question from one the Big Multi National IT Companies.

Then the next debate started that we should not ask such questions in interview. But one of the member defended saying that SORTing Algorithms are asked by interviewers to eliminate at the very first stage of the selection process.

Ok, leaving behind the debate, whether such interview questions actually test the programming skill or not, everyone agreed to find a solution.

Also Read: Open SQL, CDS or AMDP, which Code to Data Technique to use?

We received 2 approaches with working code snippet.

Solution 1 – Provided by Sercan Küçükdemirci

<code>TYPES: tt_int TYPE TABLE OF i WITH EMPTY KEY.

DATA(it_one) = VALUE tt_int( ( 1 ) ( 9 ) ( 12 ) ( 16 ) ).
DATA(it_two) = VALUE tt_int( ( 4 ) ( 10 ) ( 15 ) ( 20 ) ).

DATA:it_three TYPE tt_int.

APPEND LINES OF it_one TO it_three.
APPEND LINES OF it_two TO it_three.

DATA(lv_len) = lines( it_three ).
DATA(i) = 1.
WHILE i &lt; lv_len.
  DATA(lv_min) = it_three[ i ].
  DATA(j) = i + 1.
  WHILE j &lt; lv_len + 1 .
    IF it_three[ j ] &lt; lv_min.
      DATA(lv_temp) = lv_min.
      lv_min = it_three[ j ].
      it_three[ j ] = lv_temp.
    ENDIF.
    j = j + 1.
  ENDWHILE.
  it_three[ i ] = lv_min.
  i = i + 1.
ENDWHILE.</code>

Team SAPYard validated the output. It is as per the requirement. Thank you Sercan!! You may connect with Sercan at LinkedIn.

Solution 2 – Provided by Stephan Koester

<code>TYPES:
  gtyt_interger        TYPE TABLE OF i WITH EMPTY KEY,
  gtyt_interger_sorted TYPE SORTED TABLE OF i with NON-UNIQUE KEY table_line.

DATA(lt_table1) = VALUE gtyt_interger( ( 1 ) ( 9 ) ( 12 ) ( 16 ) ).
DATA(lt_table2) = VALUE gtyt_interger( ( 4 ) ( 10 ) ( 15 ) ( 20 ) ).

DATA(lt_table3) = CORRESPONDING gtyt_interger_sorted( lt_table1 ).
INSERT LINES OF lt_table2 INTO TABLE lt_table3.</code>

Check the output.

Just the INSERT statement did the Magic!! Amazing solution.

Also Read:My First Program in S/4HANA

Every developer think differently. And there can be different ways to achieve the same solution. Solution to this question is a perfect example.

Our SAP Technical Group is just an example of how we can collaborate and learn every day. No question is too silly to ask. Ask, Answer, Propose, Depend, Share and Learn is the motto of our SAP Group.

This is the exact code snippet and output shared by Stephan. All of us can keep this for our quick reference for new ABAP syntax and usage.

Solution 3 – Provided by Stephan Koester

Stephan provided yet another solution without using INSERT statement. Check the below snippet.

<code>TYPES:
  gtyt_interger        TYPE TABLE OF i WITH EMPTY KEY,
  gtyt_interger_sorted TYPE SORTED TABLE OF i WITH NON-UNIQUE KEY table_line.

DATA(lt_table1) = VALUE gtyt_interger( ( 1 ) ( 9 ) ( 12 ) ( 16 ) ).
DATA(lt_table2) = VALUE gtyt_interger( ( 4 ) ( 10 ) ( 15 ) ( 20 ) ).

DATA(lt_table3) = CORRESPONDING gtyt_interger_sorted( BASE ( lt_table1 ) lt_table2 ).
* Starting with NW7.51 It is possible to prevent duplicate entries using DISCARDING DUPLICATES
* https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/index.htm

cl_demo_output=>write_data( value = lt_table1 name = 'First Table' ).
cl_demo_output=>write_data( value = lt_table2 name = 'Second Table' ).
cl_demo_output=>write_data( value = lt_table3 name = 'Third Table' ).
cl_demo_output=>display( ).</code>

He has used the new BASE key word. BASE is a functional operand position in which a database convertible to the target type can be specified. BASE is use to specify a start value base for the new structure of internal table.

You can read more about keyword BASE in SAP ABAPDOCU

Also, we can use DISCARDING DUPLICATES keywords for handling duplicate rows in component operator CORRESPONDING. Check more about DISCARDING DUPLICATES in ABAP Help.

You may follow Stephan Koester at LinkedIn. You may also check Stephan’s website Koester Consulting for more SAP blogs.

Would you like to share some weird Interview Questions you faced? Anything.. Technical or Non-Technical.

Please share.

Join our Telegram SAP Technical Discuss Group.  You need to install Telegram App first.

Please SUBSCRIBE to SAPYard’s Youtube Channel for Free End to End SAP Video Course and Training.

Check HANA-ABAP Tutorials

SORTing Algorithm – Performance Comparision

$
0
0

In one of our previous articles, we talked about the SORTing Algorithm logic which is asked in many Tier One MNCs. We showed few ways to achieve it. After going through the different SORTing method, our friend Stephan Koester from Koester-Consulting was queries to validate the performance of the different SORTs.

So, he wrote one program using the same code snippets and tried to determine which SORTing Algorithm is the most optimized one. The finding is quite interesting.

For the experiment, Stephan created the tables with rows 2 to the power n where n starting from 1 till 13. In short, the table had row counts 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192.

Guinea Pig for the Experiment – i.e The Test Data

To test the performance better, he created two tables. One in ascending order from 2 till 2 to the power of 13. And the second table in descending order from 2 to the power of 13 till 2.

Techniques Used

If you check the code below, he used 3 methods to Sort and record the time.

  1. Sorted Non Unique
  2. Sorted Unique
  3. Manual Sort Algorithm

You might also Like Our Experiment with DELETING in LOOP

Program with 3 SORT Algorithm

<code>REPORT zstkoes_sort_performance.
DATA:
  lv_time_first  TYPE timestampl,
  lv_time_second TYPE timestampl,
  lv_time_third  TYPE timestampl,
  lv_time_fourth TYPE timestampl,
  lv_time_fifth  TYPE timestampl,
  lv_time_sixth  TYPE timestampl.

DATA:
  BEGIN OF gs_time,
    count             TYPE i,
    sorted_unique     TYPE timestampl,
    sorted_nonunique  TYPE timestampl,
    manual_sort_logic TYPE timestampl,
  END OF gs_time,
  gt_times LIKE TABLE OF gs_time.

TYPES:
  tt_int TYPE TABLE OF i WITH EMPTY KEY,
  BEGIN OF gtys_integer,
    integer TYPE i,
  END OF gtys_integer,
  gtyt_interger                  TYPE TABLE OF gtys_integer WITH EMPTY KEY,
  gtyt_interger_sorted_unique    TYPE SORTED TABLE OF gtys_integer WITH UNIQUE KEY integer,
  gtyt_interger_sorted_nonunique TYPE SORTED TABLE OF gtys_integer WITH NON-UNIQUE KEY integer.

DATA:it_three TYPE tt_int.

DO 13 TIMES.
  DATA(lv_count) = 2 ** sy-index.
  DATA(lt_table1) = VALUE gtyt_interger( FOR count2 = 1 THEN count2 + 1 UNTIL count2 > lv_count ( integer = count2 ) ).
  DATA(lt_table2) = VALUE gtyt_interger( FOR count = lv_count THEN count - 1 UNTIL count = 0 ( integer = count ) ).

  IF sy-index EQ 2.
    cl_demo_output=>begin_section( title = |Sample table| ).
    cl_demo_output=>write_text( text = |All tables follow the same rule:{
                                        cl_abap_char_utilities=>newline }First table: 1 to count{
                                        cl_abap_char_utilities=>newline }Second table: count to 1| ).
    cl_demo_output=>write_data( value = lt_table1 name  = |First Table| ).
    cl_demo_output=>write_data( value = lt_table2 name  = |Second Table| ).
    cl_demo_output=>begin_section( title = 'Time needed' ).
  ENDIF.

* 1. Sorted Non Unique
  GET TIME STAMP FIELD lv_time_first.
  DATA(lt_table4) = CORRESPONDING gtyt_interger_sorted_nonunique( BASE ( lt_table1 ) lt_table2 ).
  GET TIME STAMP FIELD lv_time_second.

* 2. Sorted Unique
  GET TIME STAMP FIELD lv_time_third.

* DISCARDING DUPLICATES can be used with NW7.51 and higher
  DATA(lt_table3) = CORRESPONDING gtyt_interger_sorted_unique( lt_table1 DISCARDING DUPLICATES ).
  lt_table3 = CORRESPONDING #( BASE ( lt_table3 ) lt_table2 DISCARDING DUPLICATES ).
  GET TIME STAMP FIELD lv_time_fourth.

  DATA(it_one) = VALUE tt_int( FOR count2 = 1 THEN count2 + 1 UNTIL count2 > lv_count ( count2 ) ).
  DATA(it_two) = VALUE tt_int( FOR count = lv_count THEN count - 1 UNTIL count = 0 ( CONV #( count ) ) ).

  CLEAR: it_three.
  GET TIME STAMP FIELD lv_time_fifth.
  APPEND LINES OF it_one TO it_three.
  APPEND LINES OF it_two TO it_three.

* 3. Manual Sort Logic
  DATA(lv_len) = lines( it_three ).
  DATA(i) = 1.
  WHILE i &lt; lv_len.
    DATA(lv_min) = it_three[ i ].
    DATA(j) = i + 1.
    WHILE j &lt; lv_len + 1 .
      IF it_three[ j ] &lt; lv_min.
        DATA(lv_temp) = lv_min.
        lv_min = it_three[ j ].
        it_three[ j ] = lv_temp.
      ENDIF.
      j = j + 1.
    ENDWHILE.
    it_three[ i ] = lv_min.
    i = i + 1.
  ENDWHILE.

  GET TIME STAMP FIELD lv_time_sixth.

* Populating the table with the time for the 3 techniques
  gt_times = VALUE #( BASE gt_times ( count = lv_count
                                      sorted_unique = lv_time_fourth - lv_time_third
                                      sorted_nonunique = lv_time_second - lv_time_first 
                                      manual_sort_logic = lv_time_sixth - lv_time_fifth ) ).
ENDDO.

cl_demo_output=>write_data( value = gt_times ).
cl_demo_output=>display( ).</code>

Result

Let us run and check the time taken for each Sort Algorithm

As expected, the Manual Sorting algorithm took exponential time compared to the standard syntax. The SORTED_UNIQUE fared better than SORTED_NONUNIQUE. Although the time difference is quite minuscule but SORTED UNIQUE technique was consistently better than SORTED NON UNIQUE and with more volume of data, the gap between UNIQUE and NON UNIQUE would be more visible and impactful.

Another Experiment Why is PER in conditions not always 1 ?

Keyword – DISCARDING DUPLICATES

We already mentioned about the new key word DISCARDING in previous article. It is available from NW7.51. Check below, NW 7.4 does not recognize it.

SAP Netweaver 740
DISCARDING keyword available after NW751

Keywords – CORRESPONDING, BASE, DISCARDING DUPLICATES

If you need a reference code snippet to populate table with Non Unique and Unique data, check the below program provided by Stephan. He has created two internal tables and then inserted data of both the tables into one. One of the final table is SORTED UNIQUE and the other one is SORTED NON UNIQUE. This is a good example for everyone to learn modern Syntaxes and be more efficient at their work place.

<code>REPORT zstkoes_sort.
TYPES:
  BEGIN OF gtys_integer,
    integer TYPE i,
  END OF gtys_integer,
  gtyt_interger                  TYPE TABLE OF gtys_integer WITH EMPTY KEY,
  gtyt_interger_sorted_unique    TYPE SORTED TABLE OF gtys_integer WITH UNIQUE KEY integer,
  gtyt_interger_sorted_nonunique TYPE SORTED TABLE OF gtys_integer WITH NON-UNIQUE KEY integer.

DATA(lt_table1) = VALUE gtyt_interger( ( integer = 9 ) ( integer = 1 ) ( integer = 9 ) ).
DATA(lt_table2) = VALUE gtyt_interger( ( integer = 20 ) ( integer = 9 ) ( integer = 4 ) ).

* CORRESPONDING can be used with NW7.40PL2 and higher
DATA(lt_table4) = CORRESPONDING gtyt_interger_sorted_nonunique( BASE ( lt_table1 ) lt_table2 ).

* DISCARDING DUPLICATES can be used with NW7.51 and higher
DATA(lt_table3) = CORRESPONDING gtyt_interger_sorted_unique( lt_table1 DISCARDING DUPLICATES ).
lt_table3 = CORRESPONDING #( BASE ( lt_table3 ) lt_table2 DISCARDING DUPLICATES ).

cl_demo_output=>write_data( value = lt_table1 name  = |First Table| ).
cl_demo_output=>write_data( value = lt_table2 name  = |Second Table| ).
cl_demo_output=>write_data( value = lt_table3 name  = |Sorted Table with unique key| ).
cl_demo_output=>write_data( value = lt_table4 name  = |Sorted Table with non-unique key| ).

cl_demo_output=>display( ).</code>

Output

Also TakeFree Video Course on New Syntax in ABAP 7.4+

We would like to thank Stephan for all his time and effort in providing us the code snippet and the screenshots. You may connect with Stephan at LinkedIn or visit his company website KCO for any SAP Consulting Work.

Your Feedback Please..

5300+ SAP Technical Practitioners from 6 Continents of the SAP World. Install Telegram app and click this link to join – Telegram SAP Technical Discuss Group. 

Please SUBSCRIBE to SAPYard’s Youtube Channel for Free End to End SAP Video Course and Training.

Check HANA-ABAP Tutorials


How to Get Data from Web Page using SAP ABAP?

$
0
0

You know that we have one of the largest SAP Technical Telegram group with more than 5300 Consultants and growing each passing day. Ask-Answer, Propose-Defend, Share & Learn is our motto. Every day numerous questions are asked, few get answered and many are left open for further research and analysis. Every day we learn something new and we always have something to ponder over.

In one of these discussions, one member had a need to read data from Web. This article is a result of the sample code snippet I wrote to help him.

This is the selection screen of my test program to read data from a Web Page.

I have tried to use all the new syntax of ABAP and would like the freshers in new ABAP to look into below snippets.

1. String Concatenation Operation with COND #

<code>      lv_url = |http{
        COND #(
          WHEN p_ssl EQ abap_true
          THEN |s| ) }://www.koester-consulting.com/gEtDaTaFrOmWeB.php{
            COND #(
              WHEN p_id IS NOT INITIAL
              THEN |?id={ p_id }| ) }|.</code>

Check, concatenation is done using pipe (|). Also check the COND # which can be included in the string literal operations too. When P_SSL has data then ‘s’ is concatenated after ‘http’.

New to new syntax of ABAP? Take this Free Video Course on New Features of ABAP 7.4 +

Similarly when P_ID is passed, the value of P_ID which come from selection screen is concatenated at the End.

Let’s check the Output.

1. When nothing is passed from the Selection Screen.

LV_URL = ‘ttp://www.koester-consulting.com/gEtDaTaFrOmWeB.php’.

2. When the P_ID is passed and P_SSL is Selected.

‘s’ is appended after http to make it https. Also, ‘?id=1’ is appended to the end of the URL.

LV_URL = ‘https://www.koester-consulting.com/gEtDaTaFrOmWeB.php?id=1’.

2. Inline Declaration in Class Method Calls

We need to note that, Inline Declaration is not possible for Function Module calls.

3. How to Convert JSON data to ABAP Internal Table?

<code>            /ui2/cl_json=>deserialize(
              EXPORTING
                json  = lv_data
              CHANGING
                data  = gt_customer ).</code>

If you don‘t know how the deep structure needs to look like, you can use the below code snippet to learn more about the received data and prepare your CHANGING structure TYPE accordingly.

<code>TRY.
DATA: lo_data TYPE REF TO data.

DATA(lo_json) = NEW /ui2/cl_json( ).

lo_json->deserialize_int(
EXPORTING
json = lv_data
CHANGING
data  = lo_data ).

CATCH cx_sy_move_cast_error .
ENDTRY.</code>

Once you have the lo_data, check it in debug mode and then re-write your code to declare the TYPE you need.

Also ReadCalculator in SAP using New ABAP Syntax

4. How to Convert ABAP Internal Table data to JSON?

<code>lv_json_body =  /ui2/cl_json=>serialize(
                  data     =  ls_abap_data
                 pretty_name = /ui2/cl_json=>pretty_mode-camel_case ) .</code>

Although serialize method was not needed in our sample report to read Web Data, but since we were talking about JSON to ABAP Internal table, I thought it would be useful if we also showed the other way around.

<code>TYPES:
  BEGIN OF ts_deep_entity,
    mat_doc    TYPE matnr,
    doc_year   TYPE gjahr,
    pstng_date TYPE sy-datum,
    ernam      TYPE sy-uname,

  END OF ts_deep_entity.

TYPES: BEGIN OF ty_head ,
         d TYPE ts_deep_entity,
         e TYPE ts_deep_entity,
       END OF ty_head.

*======================ABAP Data==================================
DATA(ls_abap_data) = VALUE ty_head(   d-mat_doc = '4900000814'  e-mat_doc = '4900000815'
                                     d-doc_year   = '2019'     e-doc_year   = '2020'
                                     d-pstng_date = '20190701' e-pstng_date = '20200701'
                                     d-ernam     =  'kuldeep'  e-ernam     =  'joshi'
                            ).
*======================ABAP to JSON==================================
DATA(lv_json_body) =  /ui2/cl_json=>serialize(
   data     =  ls_abap_data
   pretty_name = /ui2/cl_json=>pretty_mode-camel_case ) .</code>

If you look closely, the JSON data has been formatted in CamelCase.

Similarly, we can format the JSON output in different ways:

  • Camel Case
  • Extended
  • Low Case
  • None
  • User
  • User Low Case

We will dedicate a separate article on ABAP to JSON and JSON to ABAP format. Today, we wanted to learn a simple way to read data from Web and display in SAP ABAP. With all the above knowledge, we have written the below program. Let us test it.

Also Read A to Z of AL11 Operations

Selection Screen

Output 1

Input 2 and Output 2

Complete Working Code for Reference

<code>REPORT zstkoes_get_data_from_web.

TYPES:
  BEGIN OF gty_customer,
    id        TYPE string,
    firstname TYPE string,
    name      TYPE string,
    country   TYPE string,
  END OF gty_customer .
TYPES:
  gtty_customer    TYPE TABLE OF gty_customer .

CONSTANTS:
  gc_proxy_host    TYPE string VALUE 'PROXY_HOST',
  gc_proxy_service TYPE string VALUE '8090'.

DATA:
  gs_customer TYPE gty_customer,
  gt_customer TYPE gtty_customer.

SELECTION-SCREEN:
  BEGIN OF LINE,
    COMMENT 1(15) c_id.
PARAMETERS:
  p_id     TYPE string VISIBLE LENGTH 2.
SELECTION-SCREEN END OF LINE.

SELECTION-SCREEN: COMMENT /1(50) c_info,
                  SKIP,
                  BEGIN OF LINE.
PARAMETERS: p_ssl TYPE xfeld.
SELECTION-SCREEN: COMMENT 3(50) c_ssl,
                  END OF LINE.

SELECTION-SCREEN: BEGIN OF LINE.
PARAMETERS: p_proxy TYPE xfeld.
SELECTION-SCREEN: COMMENT 3(50) c_proxy,
                  END OF LINE.


*----------------------------------------------------------------------*
* INITIALIZATION.
*----------------------------------------------------------------------*
INITIALIZATION.
  c_id    = 'ID'.
  c_info  = 'Leave empty to get a list of customer.'.
  c_ssl   = 'Use SSL (HTTPS)'.
  c_proxy = 'Use Proxy'.

*----------------------------------------------------------------------*
* START-OF-SELECTION.
*----------------------------------------------------------------------*
START-OF-SELECTION.

  TRY.
      DATA: lv_url TYPE string.

      lv_url = |http{
        COND #(
          WHEN p_ssl EQ abap_true
          THEN |s| ) }://www.koester-consulting.com/gEtDaTaFrOmWeB.php{
            COND #(
              WHEN p_id IS NOT INITIAL
              THEN |?id={ p_id }| ) }|.

      cl_http_client=>create_by_url(
        EXPORTING
          url                 = lv_url
          proxy_host          = COND #(
                                  WHEN p_proxy EQ abap_true
                                  THEN gc_proxy_host    )
          proxy_service       = COND #(
                                  WHEN p_proxy EQ abap_true
                                  THEN gc_proxy_service )
          sap_username        = sy-uname
        IMPORTING
          client              = DATA(lo_client)
        EXCEPTIONS
          argument_not_found  = 1
          plugin_not_active   = 2
          internal_error      = 3
          OTHERS              = 4 ).
      IF lo_client IS BOUND.
        lo_client->send(
          EXCEPTIONS
            http_communication_failure = 1
            http_invalid_state         = 2
            http_processing_failed     = 3
            OTHERS                     = 4 ).
        IF sy-subrc NE 0.
          lo_client->get_last_error(
            IMPORTING
              message = DATA(cv_error_msg) ).
          MESSAGE cv_error_msg  TYPE 'S' DISPLAY LIKE 'E'.
          RETURN.
        ENDIF.

        lo_client->receive(
          EXCEPTIONS
            http_communication_failure = 1
            http_invalid_state         = 2
            http_processing_failed     = 3
            OTHERS                     = 4 ).
        IF sy-subrc NE 0.
          lo_client->get_last_error(
            IMPORTING
              message = cv_error_msg ).
          MESSAGE cv_error_msg  TYPE 'S' DISPLAY LIKE 'E'.
          RETURN.
        ENDIF.

        lo_client->response->get_status(
          IMPORTING
            code = DATA(lv_status) ).

        IF lv_status = 200.
          DATA(lv_data) = lo_client->response->get_cdata( ).
          IF lv_data IS INITIAL.
            MESSAGE 'No Data found' TYPE 'S' DISPLAY LIKE 'E'.
            RETURN.
          ENDIF.
* Copy JSON-Data into internal table
          IF p_id IS INITIAL.
            /ui2/cl_json=>deserialize(
              EXPORTING
                json  = lv_data
              CHANGING
                data  = gt_customer ).
          ELSE.
            /ui2/cl_json=>deserialize(
              EXPORTING
                json  = lv_data
              CHANGING
                data  = gs_customer ).
          ENDIF.
        ENDIF.

      ENDIF.
    CATCH cx_root.
  ENDTRY.

*----------------------------------------------------------------------*
* END-OF-SELECTION.
*----------------------------------------------------------------------*
END-OF-SELECTION.

* Output the data
  TRY.
      IF p_id IS NOT INITIAL.
        APPEND gs_customer TO gt_customer.
      ENDIF.
      CALL METHOD cl_salv_table=>factory
        IMPORTING
          r_salv_table = DATA(gref_salv)
        CHANGING
          t_table      = gt_customer.
      gref_salv->display( ).
    CATCH cx_salv_msg .
  ENDTRY.</code>

You may connect with me at LinkedIn or visit my company website KCO for any SAP Consulting Work.

Join our Group – Telegram SAP Technical Discuss. Install Telegram App and click this link. Ask – Answer, Propose – Defend, Share and Learn in our Motto.

Step by Step Tutorials on Web Services in SAP

Dynamic Code Processor

$
0
0

Do what you love, and you’ll never work another day in your life. I strongly believe in this maxim and always strive to find interesting stuff at my work place. Yes, I work in the blue screen of SAP whole day and I can proudly say it is not boring at all. Though many complain, but I love what I do. Did you know we can secretly chat using SAP ABAP? No, no.. you do not need to build another chatting tool. There is a standard function module ‘ TH_POPUP ‘ which you can use straight out of the box.   Check my other article ABAP – Power to Kill to learn more about the SAP ABAP chat.   

The point I am trying to make is, there are tons and tons of interesting activities we can perform in SAP. Do not say, you are too busy with your project work and you cannot explore. I would suggest, cut down 30 minutes from your Facebook browsing, 1 hour from going through the fake news and unnecessary messages in Whatsapp and 30 minutes from Instagram. You now have 2 hours of time to explore something new in SAP. 🙂                                             

Check how Stephan created a Simple Calculator in SAP ABAP.

With the same philosophy to try something new and explore more in SAP, we are going to learn something new and different in ABAP programming today.

Scenario:

Today morning, one of the functional consultants came to me and said that our business team is becoming crazy day by day. (well…we ABAPer says the same things for functional consultants too).. shhhhh 😜

After his statement, he revealed the requirement of business team..

Problem statement:

There should be a text box in the selection screen, in which user can put any arithmetic operations, conditional statements, string operations etc and after its execution the result should get printed. In short, they wanted to write some ABAP Commands (open SQL) in the text box and it should give the desired output.

Resolution Discussion:

After hearing this, we both laughed on the business requirement and their craziness. We thought it was a joke. But the business team members were serious. They wanted us to develop something and provide the solution.

He rested his case that day and left me pondering. I like crazy ideas. But not that crazy. 😛

Now the ball was in my court. How to provide a solution which will be dynamic and scalable? They want to write any queries in the text box and expect an output.

After all R&D, I narrowed down the below solution which treats the selection text box as a mini ABAP editor. I felt like a genious inventor after this solution. 🙂 Yes, I need to pat my back for this innovative solution.

Here user can put any ABAP query with any arithmetic operations, IF conditions, Select queries, Loops, Read statements and what not. And the beauty is, it gets executed and provides the result in output. But the syntax has to be correct when you put the query in the text box.

Check my other Article – A to Z about AL11

Excited right??…. Let’s check it out.

Resolution:

There are many ways in ABAP to do dynamic coding; like this field-symbols or dynamic internal table creation etc.

From this list, we are going to use the Subroutine Pool logic.

Let’s see, what’s the subroutine pool is all about..

Subroutine Pool:

It contains collections of subroutines that can be called externally from other programs.

Syntax of subroutine pool:

GENERATE SUBROUTINE POOL <itab> NAME <prog> [<options>].

Details:

The above statement generates a temporary subroutine pool in the main memory area of the running program.

<itab>: The code of the subroutine pool is written in internal table which is passed under <itab>.

<prog>: The generated subroutine pool is stored internally under program name <prog>.

<options>: Below options are there to pass our subroutine:

Options                                                     Description
MESSAGE <mess>  In the case of a syntax error, field <mess> contains the error message. 
INCLUDE <incl>  In the case of a syntax error, field <incl> contains the name of the include program in which the error possibly occurred. 
LINE <line>  In the case of a syntax error, field <line> contains the number of the wrong line. 
WORD <word>  In the case of a syntax error, field <word> contains the wrong word. 
OFFSET <offs>  In the case of a syntax error, field <offs> contains the offset of the wrong word in the line. 
TRACE-FILE <trac>  If you use this option, you switch on the trace mode and field <trac> contains the trace output.

So, the syntax which will be there in our program is:

<code>GENERATE SUBROUTINE POOL src
 NAME prog_name MESSAGE msg LINE line WORD word OFFSET off.</code>

Also WatchFree Video Course on New ABAP 7.4 Features

Let’s dive deep..

Program explanation:

We need to create a mother program which will take input query from the User and displays its output.

As we will create a temporary program through GENERATE SUBROUTINE option, we need to write the name and declarations for that temporary program:

In above screenshot (red box), I have provided the name as ZSB_SUBPOOL to my temporary program and stored all declaration in SRC with is of table type string. You know what SB stands for in the 2nd and 3rd character of the program name? 😛

Please note: You can give any name without Z or Y. Try giving the name as SUBPOOL and it will still work.

Next, create a subroutine named as ‘DYN1’ whose PERFORM will be called after GENERATE SUBROUTINE statement also passing the input parameter string P_FORM to SRC in that form.

Then we are appending parameter p_form to src.

At the end writing the RESULT on the screen with Write statement.

And then end the subroutine with ENDFORM.

<code>FORM DYN1 USING p_form TYPE string.
 APPEND p_form TO src.
 WRITE / RESULT.
ENDFORM.</code>

The final step is to call this temporary program via GENERATE SUBROUTINE POOL method.

This is a very powerful command. It does the syntax check and if everything is ok, it creates the temporary program in memory and executes it.

Once the Generate Subroutine Pool step is over and there is no error, it will call the program with logic written in PERFORM ‘DYN1’.

If you input the above text in the selection screen and debug the code, you will get to see the whole code of temporary program as below:

Isn’t it cool?

Code Snippet:

<code>REPORT zdynamic_query_sb.
 
 TYPES:
   source_line(256) TYPE c.
 
 DATA:
   src           TYPE TABLE OF source_line,
   prog_name(30) TYPE c,
   msg(120)      TYPE c,
   line(10)      TYPE c,
   word(10)      TYPE c,
   off(3)        TYPE c.
 
 APPEND 'PROGRAM SUBPOOL.' TO src.
 APPEND 'DATA RESULT TYPE string.' TO src.
 APPEND 'DATA: a TYPE DECFLOAT34 VALUE ''5.0''.' TO src.
 APPEND 'DATA: b TYPE DECFLOAT34 VALUE ''4.5''.' TO src.
 
 PARAMETERS p_form TYPE string.
 
 APPEND 'FORM DYN1 USING p_form TYPE string.' TO src.
 APPEND p_form TO src.
 APPEND ' WRITE / RESULT.' TO src.
 APPEND 'ENDFORM.' TO src.
 
 GENERATE SUBROUTINE POOL src
     NAME prog_name MESSAGE msg LINE line WORD word OFFSET off. 
 IF sy-subrc &lt;> 0.
   WRITE: / 'Error during generation in line', line,
         / msg, / 'Word:', word, 'at offset', off.
 ELSE.
   PERFORM dyn1 IN PROGRAM (prog_name) USING p_form.
 ENDIF.</code>

Time to Test

Execute the program and we will get below selection screen:

Insert any sample ABAP query to get its result.

Please Note: RESULT should always be the variable name for our case which you want to print.

After execution, we will get result of above query as below:

APPEND 'DATA: a TYPE DECFLOAT34 VALUE ''5.0''.' TO src.
APPEND 'DATA: b TYPE DECFLOAT34 VALUE ''4.5''.' TO src.

Also, you can access the global variable which you defined in the memory program. Here we defined A and B variables in the the program. We can provide them on selection screen text field and can access them in memory program as well.

The value of variable A is ‘5.0’ whereas the value of B is ‘4.5’ so the RESULT of above query will be ‘0.5’.

Some random Quiz questions:

  1. What would be the output of this text input?
    RESULT = ‘HELLO SAPYARD’. in the text field?
  2. Do you this this input will work?
    data(ls_1) = ‘Swapna’. data(ls_2) = ‘Dream’. Concatenate ls_1 ls_2 into RESULT SEPARATED BY SPACE.
  3. What would be the output for this input text?
    data(ln_1) = 10. data(ln_2) = 5. RESULT = ln_1 * ln_2

Solution

Straight String Operation.
Concatenate works fine.
errrr!!!! You missed the period at the end. Syntax error. 🙂

In this program you can write any ABAP query like from arithmetic operations, conditional operations or even select query too. It Magic. Right?

So what are you waiting for? Copy the program and enjoy this mini ABAP query processor which you just created.🤓

SAP is fun. We just need to find out how to have fun!!

Comments Please.

5300+ SAP Technical Practitioners from 6 Continents of the SAP World. Install Telegram app and click this link to join – Telegram SAP Technical Discuss Group. 

Please SUBSCRIBE to SAPYard’s Youtube Channel for Free End to End SAP Video Course and Training.

Check HANA-ABAP Tutorials

Useful Trick to Search “Text” in Smartforms/Text modules

$
0
0

You may master the new programming models using Core Data Services. You may build beautiful Fiori and UI5 applications which are sleek, fast and mobile friendly. You may also write SQLScript in your S/4HANA Project. But once an ABAP Programmer, always an ABAP Programmer. Clients will always have some weird requests where you will still need your basic ABAP Programming skills, debugging, analyzing and solutioning acumen.

Background: Recently we received a requirement to replace some hard-coded text written in Text Elements of Smart forms. In the SAP system there were around 350 – 400 custom smart forms to find and replace.

For e.g: I was searching for text “Secret” in which and all smartforms/textmodule its written.

Approaches/Attempts:

Approach 1:

Like many, I thought there should be a table to hold this data and fortunately (from scn community) found table STXFTXT. Field TDLINE holds smart form content and I tried searching.

Results:

voila!! I found what I was looking for. But …..

The field  TDLINE is case sensitive. 🙁 It means, you should know exact word with exact upper case and lower case combination. Otherwise you can’t get correct results in this table (I also read somewhere this table is not at all reliable).

Check, now I am searching for all lower case “secret”. It gives different output.

See here its not returning “ZDEMO” form name in a results

Also Read – JavaScript in SAP Adobe Forms

So what next? Plan B 🙂

Approach 2:

Plan B was to download smart form as XML and search for text. This way you can find the texts.

Solution Implementation:

There is a function module called “FB_CONVERT_FORM_TO_XML” which will convert form to XML by taking Form name and Form type as input. This function module accepts Text Module also as input, which is Bonus.

Now as every ABAPer does, select all forms, loop on each, get XML, convert XML to String and search your Love/Pain stories strings 🙂 and display all found form names in ALV. Isn’t this the everyday story of all ABAP developers? 🙂

Advantage of this approach:

This search is Case Insensitive. It will search for all cases.

Also Check – Trick to Find the list of Smartforms and it’s Include TEXTs (SO10)

Code Snippet for your Reference

Attached program does it. Feel free to Plug and Play . Fancy word for copy paste. 🙂

The important functions used in this sample program are: FB_CONVERT_FORM_TO_XML, SSFH_XSTRINGUTF8_TO_STRING, FB_DISPLAY_FORM, DISPLAY_XML_DOCUMENT . Class used is CL_XML_DOCUMENT and method PARSE_STRING.

The entire code is also at the bottom of this article for your quick reference.

In Selection screen, we can give Form name and String for search.

Displays all Form name/Text Module wherever search string exists, with HotSpot clicking on this will open the Smart form/Text Module in XML format. Navigate to your text element.

Please note: Inactive forms will not be converted to XML, but Function module is smart enough to say its inactive by raising Exceptions.

Now the entire Smart Form properties are in your control. Do, whatever you want.

This is my first article at SAPYard. Your genuine feedback is welcome. Please help me improve and provide you better content.

In the next article I will show how to locate (find) exact node or element where search text lies in the Smartforms. Please stay tuned.

5370+ SAP Technical Practitioners from 6 Continents of the SAP World. Install Telegram app and click this link to join – Telegram SAP Technical Discuss Group. 

Forms (Smartforms and SapScript)

Step by Step Tutorials on S/4HANA

Code Snippet

<code>REPORT zsf_find_text.
*--------------------------------------------------------------------*
* Deferred statement CLASS is used to make the class class known,
* regardless of the location of the actual definition of the class
* in the program
CLASS lcl_event_handeler DEFINITION DEFERRED.
*--------------------------------------------------------------------*
DATA:lv_string TYPE tdsfname.

SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME.
SELECT-OPTIONS:s_form FOR lv_string.
PARAMETERS:p_forms  TYPE string NO-DISPLAY,
           find_dat TYPE string LOWER CASE OBLIGATORY.
SELECTION-SCREEN END OF BLOCK b1.
*--------------------------------------------------------------------*
INITIALIZATION.
  s_form-low = 'Z*'.
  APPEND s_form.
*--------------------------------------------------------------------*
  DATA i_formname TYPE string.
  DATA e_xml      TYPE xstring.
*--------------------------------------------------------------------*
  TYPES:BEGIN OF ty_sf,
          formname TYPE tdsfname,
          formtype TYPE tdsftype,
          status   TYPE char8,
          e_xml    TYPE string,
        END OF ty_sf.
  DATA:lt_forms TYPE STANDARD TABLE OF ty_sf.
*--------------------------------------------------------------------*
* Class Definition
CLASS lcl_event_handeler DEFINITION.
  PUBLIC SECTION.
*   Hotspot Handling
    METHODS: handle_hotspot FOR EVENT link_click OF cl_salv_events_table
      IMPORTING
          row
          column  .
ENDCLASS.
*--------------------------------------------------------------------*
* Class Implementation
CLASS lcl_event_handeler IMPLEMENTATION.

* Handling Hotspot Click
  METHOD handle_hotspot.

    READ TABLE lt_forms INTO DATA(ls_forms) INDEX row.
    IF sy-subrc = 0.

      IF column = 'E_XML'.

        DATA: gcl_xml TYPE REF TO cl_xml_document.

        CREATE OBJECT gcl_xml.

*Parses XML String to DOM
        CALL METHOD gcl_xml->parse_string
          EXPORTING
            stream = ls_forms-e_xml.
*Display XML
        CALL METHOD gcl_xml->display.

      ELSE.

        CALL FUNCTION 'FB_DISPLAY_FORM'
          EXPORTING
            i_formname           = ls_forms-formname
            i_formtype           = ls_forms-formtype
            i_with_dialog        = abap_false
          EXCEPTIONS
            no_name              = 1
            no_form              = 2
            no_access_permission = 3
            illegal_language     = 4
            illegal_formtype     = 5
            OTHERS               = 6.
        IF sy-subrc &lt;> 0.
* Implement suitable error handling here
        ENDIF.

      ENDIF.
    ENDIF.
  ENDMETHOD.
ENDCLASS.
*--------------------------------------------------------------------*
START-OF-SELECTION.

  SELECT formname formtype FROM stxfadm INTO TABLE lt_forms
  WHERE formname IN s_form .

*--------------------------------------------------------------------*
  LOOP AT lt_forms ASSIGNING FIELD-SYMBOL(&lt;ls_forms>).

    DATA(lv_tabix) = sy-tabix.

    p_forms = &lt;ls_forms>-formname.
*   Convert to XML
    CALL FUNCTION 'FB_CONVERT_FORM_TO_XML'
      EXPORTING
        i_formname       = p_forms
      IMPORTING
        e_xml            = e_xml
      EXCEPTIONS
        no_active_source = 3.
    IF sy-subrc &lt;> 0.
      &lt;ls_forms>-status = 'Inactive'.
      CONTINUE.
    ENDIF.

    DATA ostr_output_data TYPE xstring.
    DATA codepage         TYPE cpcodepage.
    DATA cstr_output_data TYPE string.

*   Convert XML to String
    CALL FUNCTION 'SSFH_XSTRINGUTF8_TO_STRING'
      EXPORTING
        ostr_output_data = e_xml
      IMPORTING
        cstr_output_data = &lt;ls_forms>-e_xml
      EXCEPTIONS
        conversion_error = 1
        internal_error   = 2
        OTHERS           = 3.
    IF sy-subrc &lt;> 0.
      CONTINUE.
    ENDIF.

*   SEARCH Command
    SEARCH &lt;ls_forms>-e_xml FOR find_dat AND MARK.
    IF sy-subrc NE 0.
      DELETE lt_forms INDEX lv_tabix.
    ENDIF.
  ENDLOOP.
*--------------------------------------------------------------------*
  DATA: o_alv TYPE REF TO cl_salv_table.
  DATA: lx_msg TYPE REF TO cx_salv_msg.
  DATA: lr_columns TYPE REF TO cl_salv_columns,
        lr_column  TYPE REF TO cl_salv_column_table,
        lr_colums  TYPE REF TO cl_salv_columns_table,
        lr_display TYPE REF TO cl_salv_display_settings,
        title      TYPE lvc_title.
*--------------------------------------------------------------------*
  TRY.
      cl_salv_table=>factory(
        IMPORTING
          r_salv_table = o_alv
        CHANGING
          t_table      = lt_forms ).
    CATCH cx_salv_msg INTO lx_msg.
  ENDTRY.
*--------------------------------------------------------------------*
  lr_display = o_alv->get_display_settings( ).
  title = 'Search Results for Text:'(001) &amp;&amp; | " | &amp;&amp; find_dat &amp;&amp; | " |.
  lr_display->set_list_header( title  ).
  lr_display->set_striped_pattern( if_salv_c_bool_sap=>true ).
  lr_columns = o_alv->get_columns( ).
  lr_columns->set_optimize( abap_true ).
  DATA(gr_functions) = o_alv->get_functions( ).
  gr_functions->set_all( abap_true ).
  lr_colums  = o_alv->get_columns( ).
  lr_column ?= lr_colums->get_column( 'FORMNAME' ).
  lr_column->set_cell_type( if_salv_c_cell_type=>hotspot ).
  lr_column ?= lr_colums->get_column( 'STATUS' ).
  lr_column->set_long_text( 'Status' ).
  lr_column->set_short_text( 'Status' ).
  lr_column->set_medium_text( 'Status' ).
  lr_column ?= lr_colums->get_column( 'FORMTYPE' ).
  lr_column->set_visible( if_salv_c_bool_sap=>false ).
  lr_column ?= lr_colums->get_column( 'E_XML' ).
  lr_column->set_cell_type( if_salv_c_cell_type=>hotspot ).

*--------------------------------------------------------------------*
  DATA:co_report TYPE REF TO lcl_event_handeler.
  DATA: lo_events TYPE REF TO cl_salv_events_table.

  CREATE OBJECT co_report.
* All events
  lo_events = o_alv->get_event( ).
*
* Event handler
  SET HANDLER co_report->handle_hotspot FOR lo_events.
  o_alv->display( ).
*--------------------------------------------------------------------*</code>

How to SPLIT Data in FOR LOOP Using Modern ABAP Syntax?

$
0
0

SAP ABAP Developers are familiar with LOOP — ENDLOOP syntax. FOR Loop is relatively new to ABAPers, though other programming language use it very commonly. Every developer has seen this syntax in some or other programming language: for (i=1; i<=3; i++). Why would ABAP stay behind?

But old habits die heard. The lazy developers who were comfortable with traditional ABAP programming find it challenging to use the new syntax. In this article today, we would discuss one requirement which we can easily achieve in old ABAP, but needed some help for the new syntax.

This post is again the result of the question we had in our SAP Technical Telegram Group. One of our very active and helpful members, Wesley Massini had a program where he was reading data from a file with fixed length. It was working fine and with no issue. But later on, the business decided to separate the data with underscore “_” and the file data is not fixed any more. They want to separate the data into columns at the delimiter underscore. This delimiter can be anything viz comma(,), tilds (~), pipe (|) etc.

Sounds an easy requirement right? But can you do using the new ABAP Syntax? 🙂

Also TakeFree Video Course on New Syntax in ABAP

Existing Code for Fixed Length Data

The data in the file would look like this:

It is a file with heterogeneous data. The data in the first row and second row are different. For example, the first two fields (yellow and blue) in both the rows are Plant and Material. But the third field in red is Vendor is first row and while it is an indicator in second row. Similarly fourth field in green is a custom type in first row and some category in second row. The idea is, all the data rows in the file for type first should go to one internal table and all rows like that of second type should go into another internal table.

We used SWITCH statement in new syntax and separated the rows based on the length of the data in the file (which is fixed). Row type one has 26 characters while row type two has 18 characters. Also, we do not need to show the extension .txt.

As author and ABAPer Paul Hardy says, CDS is ABAP in Steriods. We say, not only CDS, the new ABAP itself looks like super active ABAP. 🙂

Check the above screenshot. We have In-line data declaration, usage of VALUE, FOR Loop in internal table, SWITCH and #. Do a F4 on each of these keywords and try to understand the concept.

For our case, we have know the TYPE, therefore they come after the VALUE operator. Also after SWITCH there is #. The table types are declared like below.

In order to display the output, we can write the below syntax.

Output

How to Handle Blank Rows?

We are not sure, how to avoid the blank rows in both the tables using the modern syntax. We used a DELETE statement explicitly after the data was populated in the internal tables.

If you know how to remove the blank lines in the FOR LOOP with SWITCH, please provide the solution in the comments section. We will update this article with your solution.

<code>* Delete Blank Lines
DELETE it_vm_data_tab WHERE werks IS INITIAL.
DELETE it_pm_data_tab WHERE werks IS INITIAL.</code>

Also Read My First Program in S/4HANA

New Requirement

Earlier our file was fixed length. With the business change, we started receiving file with a delimiter underscore “_”. So, we did not need to worry about the length of each fields. But again, we struggled to figure out the best way to SPLIT at “_” using new ABAP.

Solution 1

After some research and with our new knowledge, we achieved the functionality using STRLEN, SUBSTRING_BEFORE, SUBSTRING_AFTER , sub, occ string functions along with the VALUE and FOR Loop and COND keywords.

Note: The key word occ is for Occurrence. You might see a negative number for occ = -1. If occ is positive value, the occurrences are counted from the left while if occ is a negative number, it is counted from the right side.

Similarly check the keyword sub. It is for Substring. Sub looks for the characters in the string.

<code>DATA  result TYPE string.
result = substring( val = 'ABCDEFGH' off = 2 len = 2 ). 
result = substring_from( val = 'ABCDEFGH' sub = 'CD' ).
result = substring_after( val = 'ABCDEFGH' sub = 'CD' ).
result = substring_before( val = 'ABCDEFGH' sub = 'CD' ).
result = substring_to( val = 'ABCDEFGH' sub = 'CD' ).</code>

The above snippet is from SAP Help to show the usage of SUB and different SUBSTRING functions. The output for each result is “CD”, “CDEFGH”, “EFGH”, “AB”, and “ABCD” in the same order.

The above code also gave a blank line and we were forced to use the DELETE statement explicitly. Also, we had COND in the syntax which could be avoided. We achieved the business requirement, but we were still wondering if there is a better way to achieve it. Without the DELETE statement and COND. And guess what? Stephan gave a flawless solution yet again!!

Also CheckHow I Created My First SAP OData Services

Solution 2 from Stephan

Stephan recommended to put an identifier in the beginning of the data row for the file. For our case, it is VM and PM. Also, with his new solution logic there is no need to use COND.

Check the solution above. He has used WHERE clause in the FOR Loop. There is no need for WHEN and THEN. Also, the STRLEN check of each row is prevented along with COND #.

Code Snippet

<code>* Types Declaration
TYPES:
  BEGIN OF ty_vm_file,
    werks TYPE werks_d,
    matnr TYPE matnr,
    lifnr TYPE lifnr,
    ztype TYPE char1,
  END OF ty_vm_file,

  BEGIN OF ty_pm_file,
    werks TYPE werks_d,
    matnr TYPE matnr,
    htype TYPE char1,
    zcagn TYPE char2,
  END OF ty_pm_file,

  BEGIN OF ty_filename,
    filename TYPE text1024,
  END OF ty_filename,

* Table Type declaration
  tt_vm_tab       TYPE TABLE OF ty_vm_file WITH EMPTY KEY,
  tt_pm_tab       TYPE TABLE OF ty_pm_file WITH EMPTY KEY,
  tt_filename_tab TYPE TABLE OF ty_filename WITH EMPTY KEY.

* My recommendation is to put an identifier at the beginning of the filename like in this sample.
* With this logic there is no need for usage of COND like in the first sample.
DATA(it_fillename_data_tab) = VALUE tt_filename_tab( ( filename = 'VM_CA02_0074203_0000102207_H.txt' )
                                 ( filename = 'PM_CA02_0074203_C_HA.txt' ) ).

DATA(it_vm_data_tab) = VALUE tt_vm_tab( FOR ls_filename IN it_fillename_data_tab
                            WHERE ( filename(3) EQ 'VM_' )
                            ( werks = substring_after( val = substring_before( val = ls_filename-filename sub = '_' occ = 2 ) sub = '_' occ = 1 )
                              matnr = substring_after( val = substring_before( val = ls_filename-filename sub = '_' occ = 3 ) sub = '_' occ = 2 )
                              lifnr = substring_after( val = substring_before( val = ls_filename-filename sub = '_' occ = 4 ) sub = '_' occ = 3 )
                              ztype = substring_after( val = substring_before( val = ls_filename-filename sub = '.' occ = -1 ) sub = '_' occ = -1 )
                                               ) ).

DATA(it_pm_data_tab) = VALUE tt_pm_tab( FOR ls_filename IN it_fillename_data_tab
                       WHERE ( filename(3) EQ 'PM_' )
                               ( werks = substring_after( val = substring_before( val = ls_filename-filename sub = '_' occ = 2 ) sub = '_' occ = 1 )
                                 matnr = substring_after( val = substring_before( val = ls_filename-filename sub = '_' occ = 3 ) sub = '_' occ = 2 )
                                 htype = substring_after( val = substring_before( val = ls_filename-filename sub = '_' occ = 4 ) sub = '_' occ = 3 )
                                 zcagn = substring_after( val = substring_before( val = ls_filename-filename sub = '.' occ = -1 ) sub = '_' occ = -1 )
                                                                          ) ).
* Add both table data for display
cl_demo_output=>write_data( it_vm_data_tab ).
cl_demo_output=>write_data( it_pm_data_tab ).
** Show the output
cl_demo_output=>display( ).</code>

Download SPLIT in New ABAP Program for all the three methods shown in this article and practice it.

If you have a better solution, please do share. Your feedback and comments are welcome. Please let them coming.

You may connect with with Stephan at LinkedIn or visit his company website KCO for any SAP Consulting Work.

5450+ SAP Technical Practitioners from 6 Continents of the SAP World. Install Telegram app and click this link to join – Telegram SAP Technical Discuss Group. 

Please SUBSCRIBE to SAPYard’s Youtube Channel for Free End to End SAP Video Course and Training.

Check Some Useful Code Snippets

How to Show the ALV Output and the Selection Screen on the same Screen?

$
0
0

Normally the ALV output is displayed in a new screen. Not on the same screen where you have the selection parameters and select options. But, sometimes client want to see what they input in the screen and what they get output in the ALV in the same screen. It just helps them perform better, report better and analyse better. This requirement and scenario is not new. We have solution for it from back then in 2008.

If the solution is already there, then why again a new article? SAPYard has to be different. 🙂 The earlier solution provided uses SALV class “CL_SALV_TABLE“. We will use “CL_GUI_ALV_GRID” class due to its enhanced features.

Expectations

  1. Selection Screen and ALV on the Same Screen.
  2. Add a Button on the ALV Toolbar.
  3. Handle User Command.

For more information on the usage and functioning of the class CL_GUI_ALV_GRID, you can refer to the demo programs provided by SAP with the name like “BCALV_GRID* “.

Sample programs provided by SAP on the usage of CL_GUI_ALV_GRID.

Output

Simple Selection Screen with one Select-Options S_CARRID of type SFLIGHT-CARRID

Below is the Final Output which the Client is Expecting.

Selection screen and ALV on same screen

Do you know, how to check the consistency of any ALV Report? Check this – Just a key and two clicks for ALV consistency check

Let’s Design

High Level Programming Paradigm:

In other words, Pseudo Code for this requirement. 😉

  1. Docking Container needs to be created in the INITIALIZATION event.
  2. ALV needs to be created on that Docking Container.
  3. Export the SELECTed Output Table Data to ABAP Memory (this part we do not like. But till we find a better solution, let’s use it).
  4. Import data from ABAP Memory and Generate the Output.

Global Data Declarations

<code>CLASS lcl_report DEFINITION DEFERRED.
DATA: lo_report TYPE REF TO lcl_report. "Reference object of the local class
DATA: alv_container TYPE REF TO cl_gui_docking_container, "ALV Container
      alv_grid      TYPE REF TO cl_gui_alv_grid,          "ALV Grid
      layout        TYPE lvc_s_layo.                      "Layout Options               
DATA gv_carrid TYPE sflight-carrid. "Variable for Select-options declaration.</code>

Selection Screen Declarations

<code>SELECTION-SCREEN: BEGIN OF BLOCK block_1 WITH FRAME TITLE text-001.
SELECT-OPTIONS: s_carrid FOR gv_carrid.
SELECTION-SCREEN: END   OF BLOCK block_1.</code>

Local Class Definition

<code>CLASS lcl_report DEFINITION .

  PUBLIC SECTION.
    DATA: gt_data TYPE STANDARD TABLE OF sflight.
    METHODS :
      get_data,
      generate_output,
      toolbar      FOR EVENT toolbar OF cl_gui_alv_grid
        IMPORTING e_object, "To add some buttons on the ALV toolbar
      user_command FOR EVENT user_command OF cl_gui_alv_grid
        IMPORTING e_ucomm.

ENDCLASS.</code>

Local Class Implementation

  • Method: GET_DATA
<code>CLASS lcl_report IMPLEMENTATION.
  "Method Get Data
  METHOD get_data.
*   data selection
    SELECT * FROM sflight
           INTO  TABLE me->gt_data
           WHERE carrid IN s_carrid.
    IF sy-dbcnt IS INITIAL.
      MESSAGE s398(00) WITH 'No data selected'.
    ENDIF.
*
*   Export to memory
    EXPORT data = me->gt_data TO MEMORY ID sy-cprog.
  ENDMETHOD.</code>
  • Method: GENERATE_OUTPUT
<code>"Method generate_output
  METHOD generate_output.
*   Local data
    DATA: variant TYPE  disvariant.
    DATA: repid   TYPE sy-repid.

*   Import output table from the memory and free afterwards
    IMPORT data = me->gt_data FROM MEMORY ID sy-cprog.
    FREE MEMORY ID sy-cprog.
*
*   Only if there is some data
    CHECK me->gt_data IS NOT INITIAL.

    repid = sy-repid.
    variant-report = sy-repid.
    variant-username = sy-uname.
    layout-zebra = 'X'.

    CHECK alv_container IS INITIAL.
    CREATE OBJECT alv_container
      EXPORTING
        repid     = repid
        dynnr     = sy-dynnr
        side      = alv_container->dock_at_bottom
        extension = 200.
    CREATE OBJECT alv_grid
      EXPORTING
        i_parent = alv_container.
*  ALV Specific. Data selection.
    SET HANDLER : lo_report->toolbar      FOR alv_grid.
    SET HANDLER : lo_report->user_command FOR alv_grid.
    CALL METHOD alv_grid->set_table_for_first_display
      EXPORTING
        is_layout        = layout
        is_variant       = variant
        i_save           = 'U'
        i_structure_name = 'SFLIGHT'
      CHANGING
        it_outtab        = me->gt_data.

  ENDMETHOD.</code>

Also Check Just 4 Versions of the same program to understand OOPs ABAP

  • Method: TOOLBAR

In this method, we will create one Push-Button (just as an example-no functionality) for example to save a record, in case we had created this ALV as editable.

<code>"Method Tool-bar
  METHOD toolbar.
    DATA: lv_toolbar TYPE stb_button.

* Push Button "For Example SAVE
    CLEAR lv_toolbar.
    MOVE 'FC_SAVE' TO lv_toolbar-function. "Function Code
    lv_toolbar-icon = icon_system_save.    "Save Icon
    MOVE 'Save'(100) TO lv_toolbar-text.   "Push button Text
    MOVE 'Save'(100) TO lv_toolbar-quickinfo. "Push button Quick-Info
    MOVE space TO lv_toolbar-disabled. "Push button enabled
    APPEND lv_toolbar TO e_object->mt_toolbar.
  ENDMETHOD. </code>
  • Method: USER_COMMAND

This method will be used to handle all the commands given by the user, for example if user press on the Save button we created above etc. I have kept this section as blank, you can add the logic as required.

<code>"Method User_command
  METHOD user_command.
    IF e_ucomm = ' '.

    ENDIF.
  ENDMETHOD.                    "USER_COMMAND

ENDCLASS.                    "lcl_report IMPLEMENTATION</code>

In case you set the break-point at above method and press the Save button on the ALV, the execution will stop. Please check the below screenshot:

User Command captured when clicked on “Save” button.

Finally, the Events of the program execution

<code>INITIALIZATION.
* object for the report
  CREATE OBJECT lo_report.
* generate output
  lo_report->generate_output( ).

START-OF-SELECTION.
* Get data
  lo_report->get_data( ).</code>

Conclusion

In this blog post we learnt the basics of creating an ALV on the selection screen with the usage of CL_GUI_ALV_GRID class.

If you just copy the above code snippets and paste it to your SE38 editor and activate it. Just like one of our lazy editors at SAPYard did. You will find the output like below. 🙂 Yes!! It is a working code. And you do not need to design any screen elements. 😉

Why did we need to use ABAP Memory when all data is there in the program and we are displaying them in the same program?

When the program control reaches INITIALIZATION event, all memory is REFRESHED and global data is made FREE. After all everything should be initialized in the INITIALIZATION event. That is the job of INITIALIZATION event. Therefore, we have to have the ALV data somewhere to pull it back when we are in INITIALIZATION event. Now you know why we were forced to take help of the ABAP Memory. 😛

COMMENTS & QUESTIONS PLEASE!!

Do join 6060+ SAP Technical Consultants in this Telegram SAP Technical Discuss Group. Ask, Answer and Learn is our Motto. You need to install Telegram App first in your mobile or desktop and then click the joining link.

Please SUBSCRIBE to SAPYard’s Youtube Channel for Free End to End SAP Video Course and Training.

Please follow our LinkedIn Page, LinkedIn Group, Facebook Page, Twitter and Instagram.

Save our number +1-646-727-9273 and send us a Whatsapp message ‘LEARN’ to be part of our Learning Community.

Some more Tweaks & Tips on ALV

Viewing all 66 articles
Browse latest View live