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

HCM Processes and Forms for a Newbie in ABAP HR

$
0
0

Hi All,

 

Up until now I had never worked on ABAP HR, it always did sound very different and is very different also! Always had my fears in logical databases. But then I guess I was very unaware of other interesting things that were part of SAP HR one of them being the HCM Processes and Forms.

 

Beautiful concept introduced by SAP to have SAP HR processes viewed/updated in forms with minimal coding efforts. I really got blown away by this! Entire HR processes can be viewed into custom designed forms, both in Adobe and FPM. You see the beauty of these HCM Processes and forms are that we don't have to worry about DB updates, all taken care by SAP standard. Fields and its attributes can defined in one single place and the form will display it accordingly.

 

It also has the option for integrate it with an SAP workflow, more fun added to it.

 

Its exactly like an configuration!

 

Before I forget yes we can do real coding to make it behave as per requirements, BADi makes it is further easy and before you wonder off thinking where and all, the BADi's can be created within the form itself.

 

One stop for configuring, designing, and coding.

 

One more thing these forms can also be integrated with SAP Fiori!


Editor's Block Mode

$
0
0

I think erveryone knows the block mode of the edtor where you can select a block of characters while holding down the ALT-key.

2016-04-28_15-09-35.jpg

 

Did you know, that you can replace ALL of the selected characters at once?

Just type the new text and it will be changed instantly on all selected characters.

 

See video:

 

To have some more text in here let me close with the common scn salutation:

 

Reward points if helpful

Enno

BRFplus conquers the Whirling Circle of DEATH

$
0
0

BRFplus vs the Whirling Circle of Death


Hourglass.GIF

This story has a happy ending....

 

I have been playing around with BRFplus now, for many years, ever since a spent one winter reading the SAP Press book on the subject every work lunchtime, when I was in the beer garden around the corner from the office in Heidelberg.

 

I have used it in my day to day job for various things, and wrote a chapter on the subject in my own SAP Press book. In the course of all this I used the BRF workbench in version 7.02, 7.31 and 7.40 of ABAP.

 

In every case I found the performance of the workbench to be appalling. I would spend half the time staring at the whirling circle on the screen, which appeared every time I did a drop down, or pressed enter, any sort of round trip. As for both decision tables and decision trees entering every single value takes about a million button presses this was, as can be imagined, torture, or as I had to endure one million whirling circles for every branch of the decision tree.

 

Well, times move on, and at the moment I am writing the second edition of my book and this time I have a 7.50 system to play with. That makes me a very happy bunny, but when the time came to once again turn to BRF+ I thought to myself "my new system is a test system, it is physically in Singapore". I am in Sydney and all my previous systems had been physically in Melbourne. I had worried the extra latency would make matters even worse.

 

Anyway, before I got going, I emailed the inventor of BRF+, Carsten Zeigler, and asked him had much changed between 7.40 and 7.50. He said that most improvements these days were going into the add-on product DSM, makes sense to me, SAP has to make money after all, and that would explain why the appearance of the screens had not changed between 7.40 and 7.50, when they had changed vastly between 7.02 and 7.40, but for the core BRF+ product that there was a corker of an OSS note that could vastly improve performance all over the shop.

 

This is OSS Note 2280740. I downloaded it, it had a syntax error, since I now had a direct line to SAP a new version of the note came out within 24 hours, I downloaded that and all was well.

 

I can now report that all my whirling circle woes are gone. You still have to press a lot of buttons, that will change in the future I am assured, but at least I do not have to watch my good old mate the circle whirling away all day. I don't see it at all now, once the very first time the program compiles, and never again. And this is with the extra latency.

 

So, this is wonderful news. Previously whenever I had tried to demonstrate this to anybody (the big delay) the little man inside the computer figured out what I was up to and sped up the application whilst I was screen sharing or trying to do a ScreenCam, and then slowed it back down when I was on my own again. The little men inside the computer are like that, I have the same situation at work, if someone calls me over, whatever is broken fixes itself for the duration I am staring at the screen, and the problem re-occurs when I walk off.

 

Once I solved the problem by turning my back on the screen and shouting "I'm not looking, I'm not looking!" while an accomplice recorded a video on their phone of what the user was doing. That technique worked, the problem occurred of course under those laboratory conditions.

 

Anyway, moving back to the previous poor performance of BRF+ that bad performance had given me a rather skewed negative view of Web Dynpro applications. The only two I ever used were BRF+ and the leave request, and both were very "circly".

 

And whatever happened to the hourglass anyway? What did it ever do to be consigned to the dustbin of history? I bet it is lying in a gutter somewhere, drinking meths, and complaining about the injustice of it all.

 

Anyway, it can't be Web Dynpro that was the problem, as now the BRF+ performance is fine. I still think Web Dynpro looks hideous, worse than the SAP GUI if that's possible, but that is by the by.

 

So, the moral of the story is, if you use BRF+, and have a 7.31 system or higher, apply the good old note 2280740 and see what happens. It changes about one billion classes and methods (the ones that start with FDT (Funky Decision Tool, the original name for BRF+)) all for the better.

 

So I told Cartsen that the note worked and he said great, could I write a blog about the improvements I had encountered. This has been it, though I imagine he may be somewhat puzzled by the rambling, incoherent, nature of it....

 

Cheersy Cheers

 

Paul

News about ABAP Package Concept: Naming Conventions for Package Hierarchies

$
0
0

In this blog entry I’ll describe impact of OSS note 2297645 for ABAP development: https://launchpad.support.sap.com/#/notes/2297645/ . For me this is a very important ABAP innovation. SAP Partners and software architects now can optimize existing ABAP packages according state-of-the-art development:

  • We need smaller and sometimes encapsulated packages to develop small and innovative applications like Fiori and UI5 apps.
  • We can start to split up huge development classes into smaller ones to improve the software structure. This is a chance: software is getting more transparent and can be maintained with lesser costs.
  • We encapsulate existing objects like CDS objects to prevent dangers of reuse (think of CDS for BI vs. CDS for BI for example).

 

This is now possible by keeping existing naming conventions. This allows customers and partners to create modern package hierarchies like SAP does in the following picture:

SFDT.PNG

 

The BRFplus framework is developed as subpackages of the package SFDT where all development object have a same namespace prefix FDT_. Modern SAP solution have this property (BRFplus is just an example):

  • It has a welldefined structure which helps to understand the solution and makes maintenance easier.
  • The development process becomes easier since one can define responsibilities for development object on the level of packages.
  • There is a common namespace prefix which is necessary in a complex development landscape.

 

The package concept is really cool since it allows you split up huge packages into smaller one according different concerns. The benefit is a better software modularization which is necessary for modern ABAP developemt containing of smaller applications and new interface technology (Fiori & OData), operational reporting (CDS and BW on ERP) and so on. I already blogged about it in a series of blogs, f.e. ABAP Package Concept Part 4 – How to perform package checks. Please remark that this blog is not about package concept - I will show how to use it together with existing ("legacy") naming conventions.

 

Development Classes and Naming Conventions – A look back into the Past

At the beginning of every ABAP development project development packages and naming conventions are defined. The central note https://launchpad.support.sap.com/#/notes/84282/EN described how it goes:

84282.PNG

 

I give you a small example: For FI development often development packages like /ACME/FI have been introduced and the object names started with a namespace prefix /ACMR/FI_. This is necessary to avoid duplicate objects especially in complex system landscapes. According OSS note 84282 you can define the name range a transparent table TRESN which is displayed in the following picture.

FI.PNG

 

With this entry in maintenance view V_TRESN following is guaranteed:

  • There is a check which prevents that an object starting with a different name prefix can be assigned to this package.
  • And if an object is created there is a proposal for the correct package according to the namespace prefix.

 

This is extremely useful especially in larger development projects where software is transported between different development systems and we have to avoid clashes in naming conventions. Those naming conventions also make it possible to redesigning systems by moving software into a deeper software component.

 

Please be also aware that such checks are also possible (perhaps somewhat limitated) with check programs like Code Inspector (transaction SCI). But usually those checks come much too late: Once an ABAP application is developed, object renamings are difficult and expensive.

But above TRESN definition has one disadvantage: a namespace can be assigned to a package but not a package hierarchy. So we can’t apply state of the art software structure like in the following example with SFDT: the SAP application BRFplus is contained in subpackages of the package SFDT and all objects have the same namespace prefix FDT_.

 

Unfortunately this is not possible with package SFDT_BRFPLUS because the name space prefix FDT_ can be assigned only to a single package (so far). This was changed with OSS note 2297645. No we can define a namespace prefix for a package hierarchy by choosing the value „H“ like it is done on the following picture:

FIH.PNG

The letter “H” in the picture means “hierarchical” since it allows one namespace prefix for a package hierarchy. Now we create subpackages of package /ACME/FI like /ACME/FI_ODATA, /ACME/FI_CDS… to introduce a proper software modularization and keep existing naming conventions.

 

Summary

One of the drawbacks of ABAP language is that development classes (now called packages) don’t define namespaces by themselves. Therefore we have to use namespace prefixes for development objects and checks at development time to prevent renamings.

 

Since the ABAP package concept moved on, the naming conventions for package hierarchies had to follow. And now it is here and ready to use!

ENHANCE YOUR CODING STYLE

$
0
0

     Imagine yourself in a situation where you are asked to explore about a topic by just an introductory heading.Seems like judging the contents of book by merely reading its title.Difficult,isn't it?

    Same happens for a beginner like me who at the first time gets confused between a table and a workarea. It is almost impossible to sail through the oceans of Function Modules and God forbid if you get across an OOPS object, you can find yourself hitting your head against the wall.You have to understand a code(not always yours) and if your luck runs out you have to make changes to it to meet a requirement. It sometimes also happened that it made me land into a serious confusion, while trying to understand my own code which I developed a few months ago.

 

    It was after sometime my colleague told me a secret so that no other person who works on same object faces the same fate like me. It is not a big thing but a minor thing that we all ignore or forget. It is 'POWER OF COMMENTING'. An object, however complex it maybe, becomes easy to understand if it has been properly described in comments about it.Moreover every person has a way of coding and doing things which are not easy to understand for others. Many of us find it boring and time consuming but if a new person works on an object developed by you in future,it becomes a lot easier.

  Proper commenting is a healthy practice to code. A simple comment can help you understand the reason for using a particular unknown FM or why is there a particular condition in select query. After leaving no stone unturned to understand coding,this is what I figured out about the magic of “Commenting”:


1.Increases Readability of Code

    It is always easy to go through a code if you know what a particular statement does.It can help you understand the gist quickly and understand your requirement easily.


2.Increases Navigation Speed

    Many times it happens you need to change a particular field or a particular part of code but it takes time to find its position in a lengthy code.Commenting makes it easier,just press Ctrl+F and search your field or module.


3.Avoids Confusion

    A new person can get confused if he/she sees a new object or even a select query he has no idea about.Proper comment,at proper places can help avoid that confusion.

  

   So where and what should we comment.The answer is, wherever you feel this part needs some description.For example


   In select queries:

select1.JPG

 

  To define what an include contains:


include.JPG



   To establish relationship between displayed and technical field name:

 

relation.JPG


  Or to simply make the code more understandable:

 

simple.JPG



Summary:

It is always easier to impart your knowledge of coding to others in a systematic simple way.This also benefits others in dealing with troubles and setting benchmarks for making coding a good learning exercise.


Afterall we all are here to 'LEARN AND DECODE' what SAP has to offer.Aren't we?

CL_DEMO_OUTPUT, Part 1 of 2 - Usage

$
0
0

In the ABAP Keyword Documentation, in SCN blogs or in other demos you might have recognized the usage of class CL_DEMO_OUTPUT. This blog in two parts will explain that class a little bit closer. Part 1 explains its usage while Part 2 will have a look behind the scenes.

 

Disclaimer

 

The classes described here are not intended for productive usage. You might use them in demonstration programs, local test programs or for temporary testing in productive programs. You must not use them productively in productive programs.

Motivation

 

From the beginning, ABAP lacked a simple way of outputting data for test or demonstration purposes (System.out.println(...) in Java or printf in C so to say). In the old SAP GUI days of classical dynpros you could use the MESSAGE statement for short outputs in information messages or you mainly (mis)used the WRITE statemtent for creating list output. Simple and straightforward - but only in executable programs (reports)! Already in classical dialog programs this kind of WRITE output failed. Even worse in service oriented programming using classes. And nowadays when programming ABAP in ADT for UI5 and FIORI? Don't ask. The class CL_DEMO_OUTPUT was invented as a demonstration, how this lack can be circumvented.


Methods of CL_DEMO_OUTPUT

 

The methods of class CL_DEMO_OUTPUT create simple outputs of data in example programs without the need of classical lists. The class can be used via static methods and instance methods. The following methods create output in an output stream:


  • Methods BEGIN_SECTION, NEXT_SECTION, and END_SECTION create headers and open or close header levels.

  • Methods WRITE_DATA, WRITE_TEXT, WRITE_XML, WRITE_JSON, and WRITE_HTML write different kinds of output into the output stream.

    • With method WRITE_DATA you can write elementary data objects (no reference variables), structures with elementary components, and internal tables of such line types.

    • The other methods create formated outputs of texts, XML, JSON, or HTML data.

  • Method WRITE is generic. It handles ABAP data as well as texts (in non proportional format).

 

  • Methods DISPLAY_... (available  as static methods only) work as WRITE_..., but close the current output stream and open a new one. If a SAP GUI is available, the output is displayed in a window.

  • Method LINE creates a horzontal line.

  • Method DISPLAYcloses the current output stream and opens a new one. If a SAP GUI is available, the output is displayed in a window. Optionally you can also pass data to DISPLAY as you can do for WRITE.

  • Method GET works like DISPLAY but does not display the data. Instead the formated output data are returned in a text string and can be handled further.

 

The standard output format is HTML. Optionally you can choose a simple text format with tabulators and line breaks. You choose the format with method SET_MODE for the static methods or using the input parameter MODE of the factory method NEW for the instance methods.

 

The class CL_DEMO_OUTPUT is available in release 7.03/7.31 since SP07 and in higher releases. It has a class documentation.


Code Examples

 

The most simple and common type of usage might look as follows:


SELECT *

       FROM scarr

       INTO TABLE @DATA(carriers).

 

cl_demo_output=>display( carriers ).


The output is:


out1.gif


A program using more than one static method of CL_DEMO_OUTPUT might look as follows:


SELECT *

       FROM scarr

       INTO TABLE @DATA(carriers).

CALL TRANSFORMATION id SOURCE carriers = carriers

                       RESULT XML DATA(xml).


cl_demo_output=>begin_section( `Some Text` ).

cl_demo_output=>write_text( |blah blah blah \n| &&

                            |blah blah blah| ).

cl_demo_output=>next_section( `Some Data` ).

cl_demo_output=>begin_section( `Elementary Object` ).

cl_demo_output=>write_data( carriers[ 1 ]-carrid ).

cl_demo_output=>next_section( `Internal Table` ).

cl_demo_output=>write_data( carriers ).

cl_demo_output=>end_section( ).

cl_demo_output=>next_section( `XML` ).

cl_demo_output=>write_xml( xml ).

cl_demo_output=>display( ).


Since this looks very ugly, it is better to use the instance methods instead of the static methods if you call more than 3 to 4 methods of the class within a program:


SELECT *

       FROM scarr

       INTO TABLE @DATA(carriers).

CALL TRANSFORMATION id SOURCE carriers = carriers

                       RESULT XML DATA(xml).

 

cl_demo_output=>new(

  )->begin_section( `Some Text`

  )->write_text( |blah blah blah \n| &&

                 |blah blah blah|

  )->next_section( `Some Data`

  )->begin_section( `Elementary Object`

  )->write_data( carriers[ 1 ]-carrid

  )->next_section( `Internal Table`

  )->write_data( carriers

  )->end_section(

  )->next_section( `XML`

  )->write_xml( xml

  )->display( ).


Both give the same output:


out2.gif


You might ask yourself two things:


  • How can static methods and instance methods have the same name?

    The instance methods are interface methods. Method NEW returns a reference variable of type IF_DEMO_OUTPUT. This interface is implemented by CL_DEMO_OUTPUT. The interface methods have the same names as the static methods of the class. 
           
  • Why can you chain these methods?

    For this convenience, each instance method returns the self reference me.


If you want a more simplistic output, you can switch to text mode:


SELECT *

       FROM scarr

       INTO TABLE @DATA(carriers).

 

cl_demo_output=>new( 'TEXT'

  )->display( carriers ).


out3.gif


If you want to deal with the resulting formatted data yourself, you use GET instead of DISPLAY:


SELECT *

       FROM scarr

       INTO TABLE @DATA(carriers).


DATA(html) = cl_demo_output=>get( carriers ).


cl_abap_browser=>show_html( html_string = html ).


This produces the same output as the first example above.


You can also examine and run the following programs to get a complete overview of all possiblities:


  • DEMO_USAGE_OUTPUT_STATIC
  • DEMO_USAGE_OUTPUT_INSTANCE


Examples of Usage

 

An example how CL_DEMO_OUTPUT can be used by a framework is provided by the ABAP Keyword Documentation in ADT (aka ABAP in Eclipse). If an example of the ABAP Example Library uses CL_DEMO_OUTPUT, the documentation framework allows you to execute the example and displays the output. This is done by getting the HTML output from CL_DEMO_OUTPUT  and merging it into the (non SAP GUI) documentation display.

 

out4.gif

 

Another example is quite remarkable. CL_DEMO_OUTPUT made it to the stage in SAP Teched 2013!

 

Here a snapshot from Dr. Vishal Sikka's keynote:

 

out5.jpg

 

I guess it is quite safe to assume that nobody recognized how that demo output was created  .

(B.t.w., see  AMDP, Comparison of SQLScript with Open SQL for a closer look at the performance results of that example; The bad ABAP results above come from nested SELECT loops ...).

 

 

Conclusion

 

The methods of CL_DEMO_OUTPUT can serve for quick and dirty outputs in tests or in demo programs. But the class is not made for productive usage. Particularly, CL_DEMO_OUTPUT and the framework behind are not prepared for handling large amounts of data.

 

 

Note

 

CL_DEMO_OUTPUT has a little sister CL_DEMO_INPUT that can be used for simple inputs of elementary values.



CL_DEMO_OUTPUT, Part 2 of 2 - A Look Behind

$
0
0

In this blog we will look a little bit closer behind the curtain of CL_DEMO_OUTPUT, the usage of which was introduced in Part 1.

 

Disclaimer

The classes described here are not intended for productive usage. You might use them in demonstration programs, local test programs or for temporary testing in productive programs. You must not use them productively in productive programs.

 

XML Output Stream

While it is always CL_DEMO_OUTPUT that appears in demos and examples, the most important thing lies behind: An XML-based output stream that was invented to demonstrate how ABAP data can be written device independently into a stream for further processing. This output stream is produced by class CL_DEMO_OUTPUT_STREAM. The usage of this class can look as follows:


SELECT *

       FROM scarr

       INTO TABLE @DATA(carriers).

 

DATA(stream) = cl_demo_output_stream=>open( ).


stream->write_text( iv_text   = 'XML of CL_DEMO_OUTPUT_STREAM'

                    iv_format = if_demo_output_formats=>heading

                    iv_level  = 1 ).

stream->write_data( ia_value  = carriers[ 1 ]-carrid

                    iv_name   = 'FIELD'

                    iv_format = if_demo_output_formats=>nonprop ).

stream->write_data( ia_value  = carriers

                    iv_name   = 'CARRIERS'

                    iv_format = if_demo_output_formats=>nonprop ).


DATA(xml) = stream->close( ).

 

cl_abap_browser=>show_xml( xml_xstring = xml ).

 

The XML result is:

out6.gif

  • An output stream is an object opened by static method OPEN.


  • For filling the stream, class CL_DEMO_OUTPUT_STREAM accepts texts, elementary ABAP data, structures with elementary components and internal tables of these line types. CL_DEMO_OUTPUT_STREAM interprets each ABAP data object as a table with a structured line type - an idea taken from OData. This means for each kind of possible data input the same XML structure emerges: an object with a name, a list of components and a set of rows. An elementary data object is represented like a table with one row. This format is simpler than OData and can easily handled by a consumer and, e.g., be converted into HTML.


  • An output stream can be closed by instance method CLOSE. This raises an instance event COMPLETED.


Event Handlers

CL_DEMO_OUTPUT_STREAM demonstrates how a simple XML output stream for ABAP data might look like. The next step are examples for consumers of such a stream. Such examples are the classes CL_DEMO_OUTPUT_HTML and CL_DEMO_OUTPUT_TEXT.


An example for handling the output stream with CL_DEMO_OUTPUT_HTML can look like:


SELECT *

       FROM scarr

       INTO TABLE @DATA(carriers).

 

DATA(stream) = cl_demo_output_stream=>open( ).

SET HANDLER cl_demo_output_html=>handle_output FOR stream ACTIVATION 'X'.


stream->write_text( iv_text   = 'HTML for CL_DEMO_OUTPUT_STREAM'

                    iv_format = if_demo_output_formats=>heading

                    iv_level  = 1 ).

stream->write_data( ia_value  = carriers[ 1 ]-carrid

                    iv_name   = 'FIELD'

                    iv_format = if_demo_output_formats=>nonprop ).

stream->write_data( ia_value  = carriers

                    iv_name   = 'CARRIERS'

                    iv_format = if_demo_output_formats=>nonprop ).


DATA(xml) = stream->close( ).

SET HANDLER cl_demo_output_html=>handle_output FOR stream ACTIVATION ' '.


Method HANDLE_OUTPUT of CL_DEMO_OUTPUT_HTML converts the XML stream to HTML, writes it to the ABAP memory, and displays it if a SAP GUI is available.


out7.gif


Looks familiar?


You can do the same with CL_DEMO_OUTPUT_TEXT in order to convert the XML stream to a simple text string. Methods HANDLE_OUTPUT of these classes also raise an event when the conversion is completed.


Putting it Together

Now that we have demonstration classes that show how an output stream can be created and handled we only need a framework that allows its comfortable usage. An that's what CL_DEMO_OUTPUT demonstrates.


  • CL_DEMO_OUTPUT is simply a wrapper for CL_DEMO_OUTPUT_STREAM, CL_DEMO_OUTPUT_HTML, and CL_DEMO_OUTPUT_TEXT.


  • The methods of CL_DEMO_OUTPUT
    • open and close streams
    • set the handlers
    • pass data to the methods of CL_DEMO_OUTPUT_STREAM

 

That's basically all!


Besides that, some effort goes into retrieving the names of the ABAP data in order to pass them to the stream, but that's a gimmick.


Examples for Usage

In the package SABAP_DEMOS_OUTPUT_STREAM you find the following programs that demonstrate the usage of the output stream:

 

  • DEMO_OUTPUT_STREAM
  • DEMO_USAGE_OUTPUT_STREAM


Conclusion

The demonstration classes shown here serve as an example how a simple output framework could be achieved for ABAP.

 

  • CL_DEMO_OUTPUT_STREAM creates an XML output stream. The XML format is a working example how such a stream might look like, if you do not want to use more complicated things like OData. You can easily invent other or better formats. The class CL_DEMO_OUTPUT_STREAM is a working example, for creating such a stream but its implementation is far from being marketable.

  • CL_DEMO_OUTPUT_HTML, and CL_DEMO_OUTPUT_TEXTare working examples how an XML output stream can be handled. Again the implementation is far from being suited for productive usage. If you take a class as CL_DEMO_OUTPUT_STREAM as given, you can easily go and create your own handlers for that class in order to consume the output stream.

  • Last but not least CL_DEMO_OUTPUT is nothing but a working example, how an output stream and its consumers might be used conveniently.

 

And that's that ...

Logging Expressions

$
0
0

In two preceding blogs I bragged about CL_DEMO_OUTPUT.

 

Predictably, there is some disappointment because this class is not intended for productive usage.


Fair enough. But you can use it for testing purposes.


As an example, I will show you how you can use CL_DEMO_OUTPUT to produce a logfile for an expression. Quick and dirty.


To do so I choose the following simple constructor expression.


TYPES itab TYPE STANDARD TABLE OF i WITH EMPTY KEY.

 

DATA(itab) =

  VALUE itab(

    FOR i = 1 UNTIL i > 3

    FOR j = 1 UNTIL j > 3

      ( i * 10 + j ) ).

 

Of course, you can test the result with cl_demo_output=>display( itab ).

 

But assume that you want to know what happens inside the expression and it is a more complicated expression and you don't want to debug or you can't or don't want to use other convenience tools (watchpoint, logpoints) provided by the ABAP Workbench/ADT or whatsoever.

 

In that situation you can instrumentalize an expression with CL_DEMO_OUTPUT, e.g. by misusing a LET expression:



DATA(log) = cl_demo_output=>new( ).

 

DATA(itab) =

  VALUE itab(

    FOR i = 1 UNTIL i > 3

    FOR j = 1 UNTIL j > 3

 

    LET o = log->write( |{ i }, { j }\n{ i * 10 + j } | )->line( ) IN

 

      ( i * 10 + j ) ).

 

log->display( ).



Adding these three temporary lines to the above code gives:


out8.gif


Note that the local variable o declared behind LET is not used at all. It simply serves as hook for writing into the output object referenced by log.



There are no limits to the imagination!














ABAP Program to start and stop SAP PI communication channels

$
0
0

Introduction


We recently faced an issue with JMS receiver communication channels in SAP PI which are frequently throwing error in case of network,connectivity issues or when the MQ queue manager is restarted.JMS receiver channels unable to reconnect to WebSphere - MQ queue manager and every time we need to manually restart the communication channels in case of this issue.


Error


JMS channel fails to connect to MQ whenever any network problem occurs. This generates the following error

“MQJMS1016: an internal error has occurred. Please contact your system administrator.

Detail: java.lang.NullPointerException:  in sending to destination queue: //TESTXXX”.


Root Cause


The main reason for this issue is that Queue Manager which would break all the connections that are idle for more than 10 minutes interval and whenever a network problem occurs the PI JMS channel goes down.


This problem is caused due to a limitation in all versions of Websphere MQ including 6.x that has been acknowledged by IBM. Websphere MQ diverges from the JMS specification and other JMS products in its behavior of notifying the JMS connection exception listener. (javax.jms.ExceptionListener).


When only a javax.jms.QueueSender object is created from a javax.jms.Session object associated with a javax.jms.Connection object, errors/drops in the connection do not invoke the associated exceptionlistener set on the connection object. Instead an exception is encountered only when the next JMS message (javax.jms.Message) is sent through the JMS sender.

However, when a MessageListener (asynchronous) is created on a Session object associated with a JMS Connection, WSMQ informs the connection's ExceptionListener immediately.There are no current solutions to the problem, only work arounds are provided.


As per the SAP note :0000948016 we need to create a ABAP program and set up batch jobs and schedule it to start and stop the JMS receiver communication channels in SAP PI.


https://websmp230.sap-ag.de/sap/support/notes/convert2pdf/0000948016?sap-language=EN


ABAP program Code


*&---------------------------------------------------------------------*

*& Report  ZRESTART_JMS_RCVR_CHANNEL

*&

*&---------------------------------------------------------------------*

*&

*&

*&---------------------------------------------------------------------*

 

REPORT  ZRESTART_JMS_RCVR_CHANNEL.

 

SET EXTENDED CHECK OFF.

 

DATA: client TYPE REF TO if_http_client.

DATA: cclient TYPE REF TO if_http_client.

DATA: dummy TYPE string,

      subrc TYPE sysubrc.

 

 

PARAMETERS:

            dest TYPE rfcdest.

 

DATA:

            protocol(8) TYPE c VALUE 'HTTP/1.0',

            path(128) TYPE c,

            mandant TYPE symandt,      " default sy-mandt,

            username TYPE syuname,     " default sy-uname,

            password(128) TYPE c,

            langu TYPE sylangu,

            listen TYPE c VALUE space,

            scheme     TYPE i VALUE 1.

 

DATA: host_str TYPE string,

      service_str TYPE string.

DATA:  ncolor TYPE i VALUE 4.

DATA : tx TYPE f, ta TYPE i, te TYPE i.

DATA : mintime TYPE f VALUE 1000000000.

DATA : t(10) TYPE p.

 

INCLUDE RSHTTPPIN_DISPLAY .

 

  DATA: wa_inout TYPE type_alv_entry,

        l_columns TYPE REF TO cl_salv_columns_table,

        l_column  TYPE REF TO cl_salv_column_table,

        l_status TYPE i,

        l_string TYPE string.

 

START-OF-SELECTION.

 

  GET RUN TIME FIELD ta.

  CALL METHOD cl_http_client=>create_by_destination

    EXPORTING

      destination              = dest

    IMPORTING

      client                   = client

    EXCEPTIONS

      destination_not_found    = 1

      internal_error           = 2

      argument_not_found       = 3

      destination_no_authority = 4

      plugin_not_active        = 5

      OTHERS                   = 5.

  IF sy-subrc <> 0.

    CASE sy-subrc.

      WHEN 3.

        MESSAGE s000(sr) WITH

          'Create failed: Argument not found'.       "#EC NOTEXT

      WHEN 4.

       MESSAGE s000(sr) WITH

         'Create failed: destionation no authority'. "#EC NOTEXT

      WHEN 5.

        MESSAGE s000(sr) WITH

         'Create failed: plugin not active'.         "#EC NOTEXT

      WHEN OTHERS.

        MESSAGE s000(sr) WITH 'Create failed'.       "#EC NOTEXT

    ENDCASE.

    EXIT.

  ENDIF.

 

  client->propertytype_accept_cookie = client->co_prompt.

 

  IF NOT username IS INITIAL AND

     NOT password IS INITIAL.

 

    DATA: password_str TYPE string.

    password_str = password.

    DATA: lclient TYPE REF TO cl_http_client,

           user TYPE string.

    user = username.

    lclient ?= client.

    CALL METHOD client->authenticate

      EXPORTING

        client   = mandant

        username = user

        password = password_str

        language = langu.

 

 

    "call method lclient->authentication.

    "Set request method

  ENDIF.

  CALL METHOD client->request->set_header_field

    EXPORTING

      name  = '~request_method'

      value = 'GET'.

 

  "Set request protocol

  IF protocol = 'HTTP/1.0'.

    CALL METHOD client->request->set_header_field

      EXPORTING

        name  = '~server_protocol'

        value = 'HTTP/1.0'.

  ELSE.

    CALL METHOD client->request->set_header_field

      EXPORTING

        name  = '~server_protocol'

        value = 'HTTP/1.1'.

  ENDIF.

 

  dummy = path.

  cl_http_utility=>set_request_uri( request = client->request

                                    uri     = dummy ).

  CALL METHOD client->send

    EXCEPTIONS

      http_communication_failure = 1

      http_invalid_state         = 2

      http_processing_failed     = 3

      OTHERS                     = 4.

  IF sy-subrc <> 0.

    CALL METHOD client->get_last_error

      IMPORTING

        code    = subrc

        MESSAGE = dummy.

 

    MESSAGE s000(sr) WITH dummy.

    EXIT.

  ENDIF.

 

  IF listen IS INITIAL.

    CALL METHOD client->receive

      EXCEPTIONS

        http_communication_failure = 1

        http_invalid_state         = 2

        http_processing_failed     = 3

        OTHERS                     = 4.

    IF sy-subrc <> 0.

      CALL METHOD client->get_last_error

        IMPORTING

          code    = subrc

          MESSAGE = dummy.

      MESSAGE s000(sr) WITH dummy.

      EXIT.

    ENDIF.

  ELSE.

    CALL METHOD client->listen

      RECEIVING

        client                     = cclient

      EXCEPTIONS

        http_communication_failure = 1

        http_no_open_connection    = 2.

    IF sy-subrc = 0.

      CALL METHOD client->receive

        EXCEPTIONS

          http_communication_failure = 1

          http_invalid_state         = 2.

      IF sy-subrc <> 0.

        CALL METHOD client->get_last_error

          IMPORTING

            code    = subrc

            MESSAGE = dummy.

        MESSAGE s000(sr) WITH dummy.

        EXIT.

      ENDIF.

    ELSE.

      IF sy-subrc <> 0.

        MESSAGE s000(sr) WITH 'communication error'.

        EXIT.

      ENDIF.

    ENDIF.

  ENDIF.

 

wa_inout-name = text-005.

CALL METHOD  client->response->get_status(

    IMPORTING code = l_status ).

  wa_inout-value = l_status.

  CONDENSE wa_inout-value.

  write:/ wa_inout-name, wa_inout-value.

 

  wa_inout-name = text-004.

  CALL METHOD  client->response->get_status(

    IMPORTING reason = l_string ).

  wa_inout-value = l_string.

  write:/ wa_inout-name, wa_inout-value.

 

  wa_inout-name = text-006.

  wa_inout-value = t.

  CONDENSE wa_inout-value.

  CONCATENATE wa_inout-value text-007 INTO wa_inout-value

    SEPARATED BY space.

  write:/ wa_inout-name, wa_inout-value.

 

ABAP report ‘ZRESTART_JMS_RCVR_CHANNEL’will takes HTTP Destination's [START and STOP] as input and triggers Connection test for the same. 


For every receiver JMS channel, two variants are created that would pass the StartChannelHTTP Destination and StopChannelDestination respectively. Finally, a batch job "ZRESTART_JMS_REC" is to be created with two steps.


First step is to call the Report "ZRESTART_JMS_RCVR_CHANNEL" with Stop variant.

Second step to call the Report with Start Variant. It’s scheduled to be executed a periodic intervals.


This process will prevent all Receiver JMS channels to be automatically start and stop the channels.

Step by step to visualize your CDS view via Analysis Path Framework (APF)

$
0
0

Analysis Path Framework (APF) is a framework which provides reuse components that allow you to build and enhance interactive analytical Web applications. Recently during my self study, I find out that an OData service exposed by CDS view could easily be consumed by APF.

 

Here below is detailed step.

 

For prerequisites to use APF, you can find it in SAP help.

In my case, I have the following PFCG role assigned:


clipboard1.png

And this role has the following role menu assigned:

clipboard2.png

Step1. Create a simple header and item CDS view, and a consumption view to expose as OData service.

 

Below source code is for item view:

@AbapCatalog.sqlViewName: 'zorITem'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'order item detail'
define view Z_I_Order_Item as select from zorder_item {  key zorder_item.parent_id,  key zorder_item.item_id,  zorder_item.item_text
}
Below source code for header view:
@AbapCatalog.sqlViewName: 'zorheader'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'header view'
define view Z_I_Order_Header as select from zorder_header
association [0..*] to Z_I_Order_Item as _Item
on $projection.object_id = _Item.parent_id
{  key zorder_header.object_id,  zorder_header.description,  @ObjectModel.association.type: #TO_COMPOSITION_CHILD  _Item
}

The corresponding database in ABAP for these two CDS views are listed below:

clipboard3.png

clipboard4.png

The source code of CDS view:

@AbapCatalog.sqlViewName: 'zjorderview'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Jerry order consumption view'
@OData.publish: true
define view Z_C_Order as select from Z_I_Order_Header {  key Z_I_Order_Header.object_id,  Z_I_Order_Header.description,  @ObjectModel.association.type: [#TO_COMPOSITION_CHILD]  Z_I_Order_Header._Item
}

Since I use the annotation OData.publish:true, so a corresponding OData service Z_C_Order_CDS is generated automatically.

 

Register it via tcode /IWFND/MAINT_SERVICE. Once done, ensure the metadata access could successfully be done.

clipboard5.png

Step2. Expose the created OData service via APF

 

Click tile APF Configuration Modeler,

clipboard6.png

Create a new Application:

clipboard7.png

Use the default semantic object:

clipboard8.png

Create a new configuration which acts as a container for sub settings such as Filters and Categories:

clipboard9.png

In Smart Filter, choose the OData service and entity type created from previous step.

clipboard10.png

And a new category:

clipboard11.png

clipboard12.png

Select this new category and create a new step:

clipboard13.png

In the step creation page, just select all the two properties exposed by CDS view Z_C_Order, that is:

clipboard14.png

Create a new Representation based on this step:

clipboard15.png

Here for representation type, I just choose the most simple one: Table. Choose property from drop down list for table display.

clipboard16.png

Once done, click execute button, you will see the following screen:

clipboard17.png

clipboard18.png

Then choose the table representation:

clipboard19.png

Then the table is displayed as expected:

clipboard20.png

Its content matches with the data in the corresponding database table.

clipboard21.png

Download payslip in PDF format employee wise to Application Server

$
0
0

This blog describes the option to download payslip of each employee in separate PDF files to application server. In the case of smartform, the payslip of each employee will be printed in each page of smart form output. The function module illustrated below will handle the process of downloading payslip to application server. You can call it in your custom program.


FUNCTION ZPYSLP_PDF_DWNLD_APP_SRV.

*"*"Local Interface:

*"  IMPORTING

*" REFERENCE(I_FORMNAME) TYPE  CHAR30

*" REFERENCE(I_FILENAME) TYPE  STRING

*" REFERENCE(I_PRESENTATION) TYPE  CHAR1 OPTIONAL

*" REFERENCE(I_APPLICATION) TYPE CHAR1 DEFAULT 'X'

*" REFERENCE(IT_FINAL) TYPE ZTT_PAYSLIP

*" REFERENCE(I_DATE) TYPE  CHAR20

*"  EXCEPTIONS

*" ERROR_PRESENTATION_SERVER

 

DATA:   FNAM             TYPE RS38L_FNAM,   "FORM NAME

         CONTROL_PARAMETERS TYPE SSFCTRLOP,    "CONTROL PARAMETERS

         OUTPUT_OPTIONS     TYPE SSFCOMPOP,    "OUTPUT OPTIONS

         JOB_OUTPUT_INFO    TYPE SSFCRESCL,

         DOCTAB_ARCHIVE     TYPE DOCS OCCURS 0,

         BIN_FILESIZE(132TYPE C,

         WA_DATA            TYPE TLINE,

         E_DATA_OTF         TYPE TABLE OF TLINE,

         LV_JOB_OUTPUT_INFO TYPE SSFCRESCL,

         LV_LINES           TYPE I,

         LV_COUNT(1)       TYPE N,

         LT_PAYSLIP         TYPE ZTT_PAYSLIP,

         WA_FINAL           TYPE ZST_PAYSLIP,

         LV_FILENAME        TYPE STRING,

         LV_APPFILE         TYPE STRING.

 

CLEAR LV_LINES.

DESCRIBE TABLE IT_FINAL LINES LV_LINES. “FINAL INTERNAL TABLE IN SMARTFORM



DO.

   LV_COUNT = LV_COUNT + 1.

   CLEAR: WA_FINAL, LT_PAYSLIP, LV_FILENAME, LV_APPFILE.

   REFRESH LT_PAYSLIP.

 

   IF LV_COUNT LE LV_LINES.

     READ TABLE IT_FINAL INTO WA_FINAL INDEX LV_COUNT.

     IF SY-SUBRC EQ 0.

       APPEND WA_FINAL TO LT_PAYSLIP.

     ENDIF.

   ELSE.

     EXIT.

   ENDIF.

 

 

IF I_PRESENTATION = 'X'.

     CONCATENATE 'PAYSLIP' WA_FINAL-PERNR '.PDF' INTO LV_FILENAME.

     LV_APPFILE = LV_FILENAME.

     CONCATENATE I_FILENAME LV_FILENAME INTO LV_FILENAME.

   ENDIF.

 

   IF I_APPLICATION = 'X'. "FILE NAME IN APPLICATION SERVER

     CLEAR LV_FILENAME.

     CONCATENATE I_FILENAME '/PAYSLIP' WA_FINAL-PERNR '.PDF' INTO LV_FILENAME.

   ENDIF.

 

*--INITIALIZE THE CONTROL PARAMETERS

   CONTROL_PARAMETERS-NO_OPEN     = 'X'.

   CONTROL_PARAMETERS-NO_CLOSE    = 'X'.

   CONTROL_PARAMETERS-NO_DIALOG   = 'X'.

   CONTROL_PARAMETERS-GETOTF      = 'X'.

 

 

*--INITIALIZE OUTPUT OPTIONS

   OUTPUT_OPTIONS-TDDEST    = 'LOCL'.


*--GET THE SMART FORM F.M NAME

   CALL FUNCTION 'SSF_FUNCTION_MODULE_NAME'

     EXPORTING

       FORMNAME           = I_FORMNAME

     IMPORTING

       FM_NAME            = FNAM

     EXCEPTIONS

       NO_FORM            = 1

       NO_FUNCTION_MODULE = 2

       OTHERS             = 3.



*--OPEN THE SMARTFORM

   CALL FUNCTION 'SSF_OPEN'

     EXPORTING

*       ARCHIVE_PARAMETERS =

       USER_SETTINGS      = ' '

*       MAIL_SENDER        =

*       MAIL_RECIPIENT     =

*       MAIL_APPL_OBJ      =

       OUTPUT_OPTIONS     = OUTPUT_OPTIONS

       CONTROL_PARAMETERS = CONTROL_PARAMETERS

* IMPORTING

*       JOB_OUTPUT_OPTIONS =

     EXCEPTIONS

       FORMATTING_ERROR   = 1

       INTERNAL_ERROR     = 2

       SEND_ERROR         = 3

       USER_CANCELED      = 4

       OTHERS             = 5.

 

   IF SY-SUBRC <> 0.

* IMPLEMENT SUITABLE ERROR HANDLING HERE

   ENDIF.


*--START SMARTFORM

   CALL FUNCTION FNAM

     EXPORTING

*       ARCHIVE_INDEX      =

*       ARCHIVE_INDEX_TAB  =

*       ARCHIVE_PARAMETERS =

       CONTROL_PARAMETERS = CONTROL_PARAMETERS

*       MAIL_APPL_OBJ      =

*       MAIL_RECIPIENT     =

*       MAIL_SENDER        =

       OUTPUT_OPTIONS     = OUTPUT_OPTIONS

       USER_SETTINGS      = ' '

       PAY_COMP           = LT_PAYSLIP

       DATE               = I_DATE

*     IMPORTING

*       DOCUMENT_OUTPUT_INFO       =

*       JOB_OUTPUT_INFO    =

*       JOB_OUTPUT_OPTIONS =

     EXCEPTIONS

       FORMATTING_ERROR   = 1

       INTERNAL_ERROR     = 2

       SEND_ERROR         = 3

       USER_CANCELED      = 4

       OTHERS             = 5.


*--CLOSE THE SMARTFORM

   CALL FUNCTION 'SSF_CLOSE'

     IMPORTING

       JOB_OUTPUT_INFO  = JOB_OUTPUT_INFO

     EXCEPTIONS

       FORMATTING_ERROR = 1

       INTERNAL_ERROR   = 2

       SEND_ERROR       = 3

       OTHERS           = 4.


*--CONVERT TO THE SMARTFORM OTF OUTPUT TO THE PDF

   CALL FUNCTION 'CONVERT_OTF_2_PDF'

*    EXPORTING

*      USE_OTF_MC_CMD         = 'X'

     IMPORTING

       BIN_FILESIZE           = BIN_FILESIZE

     TABLES

       OTF                    = JOB_OUTPUT_INFO-OTFDATA

       DOCTAB_ARCHIVE         = DOCTAB_ARCHIVE

       LINES                  = E_DATA_OTF

     EXCEPTIONS

       ERR_CONV_NOT_POSSIBLE  = 1

       ERR_OTF_MC_NOENDMARKER = 2

       OTHERS                 = 3.


IF I_PRESENTATION = 'X'.

     CALL FUNCTION 'GUI_DOWNLOAD'

       EXPORTING

         FILENAME                = LV_FILENAME

         FILETYPE                = 'BIN'

       TABLES

         DATA_TAB                = E_DATA_OTF

       EXCEPTIONS

         FILE_WRITE_ERROR        = 1

         NO_BATCH                = 2

         GUI_REFUSE_FILETRANSFER = 3

         INVALID_TYPE            = 4

         NO_AUTHORITY            = 5

         UNKNOWN_ERROR           = 6

         HEADER_NOT_ALLOWED      = 7

         SEPARATOR_NOT_ALLOWED   = 8

         FILESIZE_NOT_ALLOWED    = 9

         HEADER_TOO_LONG         = 10

         DP_ERROR_CREATE         = 11

         DP_ERROR_SEND           = 12

         DP_ERROR_WRITE          = 13

         UNKNOWN_DP_ERROR        = 14

         ACCESS_DENIED           = 15

         DP_OUT_OF_MEMORY        = 16

         DISK_FULL               = 17

         DP_TIMEOUT              = 18

         FILE_NOT_FOUND          = 19

         DATAPROVIDER_EXCEPTION  = 20

         CONTROL_FLUSH_ERROR     = 21

         OTHERS                  = 22.

     IF SY-SUBRC EQ 0.

*        RAISE ERROR_PRESENTATION_SERVER.

     ENDIF.

   ELSEIF I_APPLICATION = 'X'.

     OPEN DATASET LV_FILENAME FOR OUTPUT IN BINARY MODE.

     IF SY-SUBRC = 0.

       LOOP AT E_DATA_OTF INTO WA_DATA.

         TRANSFER WA_DATA TO LV_FILENAME.

       ENDLOOP.

       CLOSE DATASET LV_FILENAME.

     ELSE.

       WRITE : / 'OPERATING SYSTEM COULD NOT OPEN FILE'.

     ENDIF.

   ENDIF.

ENDDO.

ENDFUNCTION.



CALL FUNCTION MODULE IN CUSTOM PROGRAM (Driver program for SmartForm)

 

 

       CALL FUNCTION 'ZFM_PYSLP_PDF_DWNLD_APP_SRV'

         EXPORTING

           I_FORMNAME    = 'ZSF_PAYSLIP' “SMARTFORM NAME

           I_FILENAME    = '/ABCD/PYSLP' “LOCATION IN APPLICATION SERVER

*         I_PRESENTATION                  =

           I_APPLICATION = 'X'

           IT_FINAL      = IT_FINAL “FINAL INTERNAL TABLE IN SMARTFORM

           I_DATE        = LV_DATE

*       EXCEPTIONS

*         ERROR_PRESENTATION_SERVER       = 1

*         OTHERS        = 2

         .

       IF SY-SUBRC <> 0.

* IMPLEMENT SUITABLE ERROR HANDLING HERE

       ENDIF.

How to maintain the partner mobile phone when creating SO with BAPI_SALESORDER_CREATEFROMDAT2.

$
0
0

When creating sales order with BAPI_SALESORDER_CREATEFROMDAT2, we normally use the structure PARTNERADDRESSES to change the partner address data if needed. However, the "mobile phone" field is not contained in structure PARTNERADDRESSES. Means, in standard, the BAPI doesn't support maintaining the mobile phone number. It sometimes cause inconvenience in the project.

 

With this blog post, I would like to share some information about how to enhance the structure PARTNERADDRESSES to make the maintaining of mobile phone possible.


1. Enhance structure BAPIADDR1.

Firstly, we need to check the enhancement category and change it if necessary. Follow the below path to check the Enhancement category of structure.

SE11 to change structure BAPIADDR1.

Then execute MENU  >EXTRAS  >ENHANCEMENT CATEGORY

11111.png

Change it to: Can Be Enhanced (Deep).

Save and activate.



Secondly, create structure APPEND_BAPIADDR1_2 in the System and append this structure to structure BAPIADDR1.

For this purpose, proceed as follows:

Use Transaction SE11 to display structure BAPIADDR1. Via 'Goto' -> 'Append structure' the system issues a dialog box.

If you have no append structure, the dialog box will be like below. Enter 'APPEND_BAPIADDR1_2' into the input field.

22222.png

 

If you have already an append structure for the structure BAPIADDR1, the pop-up will be like below. You can use the existing one, or click on the create button to create a new one like above.

44444.jpg


After entering the Append Name and pressing enter, we come to the append structure creation screen as below. Enter the text and the field as below.

As a short text, enter 'APPEND for mobile phone number in structure BAPIADDR1'. You can enter any other text if you want.

For the "Component", I use "MOB_NUMBER" here. You can enter any value you want. It is the name of the field. You will see this name in the BAPI structure. And you need use the same field name when enhancing the interface. (I will show later.)

For the "Component type", please enter "AD_MBNMBR1".

33333.png

Save and activate the append structure.


Now, you will be able to see the MOB_NUMBER already in structure PARTNERADDRESSES as below, when executing BAPI_SALESORDER_CREATEFROMDAT2 in SE37. But it will not work, because the interface program has not been enhanced.

55555.png


2. Enhance interface program: FM ADDR_CONVERT_FROM_BAPIADDR1.

The sample code is:

* pass mobile phone number to sales order address.

   CLEAR ADTEL_WA.

   ADTEL_WA-ADTEL-TEL_NUMBER = ADDR1_COMPLETE_BAPI-MOB_NUMBER.

   IF NOT ADTEL_WA-ADTEL IS INITIAL.

     ADTEL_WA-ADTEL-FLGDEFAULT = 'X'.

     ADTEL_WA-ADTEL-R3_USER    = '3'.

     APPEND  ADTEL_WA TO ADDR1_COMPLETE-ADTEL_TAB  .

   ENDIF.


Here, you can see that I am passing the ADDR1_COMPLETE_BAPI-MOB_NUMBER value to ADTEL_WA-ADTEL-TEL_NUMBER. If you use other field name than MOB_NUMBER, then you will need to adjust the coding part accordingly.

ADTEL_WA-ADTEL-R3_USER = '3' is to tell the system that this number is the mobile phone number.


As for the place where to put the code, it is actually not important, only if you don't interrupt the existing code. The easiest way is to insert the code at the end of the FM.

I would suggest putting the coding here as below. Because it is just after the code for Telephone field. It looks beautiful.

666666.png


Activate the code.

Enhancement finished.


Now, you will be able to maintain the mobile phone number when creating sales order with BAPI_SALESORDER_CREATEFROMDAT2.

By the way, don't forget to link the address with ORDER_PARTNERS-ADDRESS = PARTNERADDRESSES-ADDR_NO.

 

Hope it is helpful.

 

Best regards,

Hualin Zhang



Code visualization using Moose

$
0
0

It is now possible to extract model information from a SAP system to analyze it using Moose in a Pharo virtual machine.

 

Pharo is a kind of Smalltalk and heavily used by specialists who develop techniques for code visualization especially for refactoring. The SAP model data is extracted using FAMIX, this allows a more detailed analysis as would be possible using UML. This is now possible due to a new open source extractor.

 

Currently extracted are for instance:
- Accesses to attributes of class

- Invocations of class methods

- Accesses to database tables

 

It is planned to add other objects like reports, functions, DSO and InfoCubes of SAP BW ...

 

Examples for diagrams are:

https://raw.githubusercontent.com/RainerWinkler/Moose-FAMIX-SAP-Extractor/master/wiki_pictures/Packages_with_classes_and_usages.png

 

https://raw.githubusercontent.com/RainerWinkler/Moose-FAMIX-SAP-Extractor/master/wiki_pictures/Classes_with_method_attr_usage.png

https://raw.githubusercontent.com/RainerWinkler/Moose-FAMIX-SAP-Extractor/master/wiki_pictures/Classes_with_names_and_method_usages_SABP_RTTI.png

This diagrams are not just images but it is possible to move the parts around, to see the names using mouse over or to explore it in detail by clicking on it.

 

You find details in this blog in "Custom Code Management":

 

http://scn.sap.com/community/abap/custom-code-management/blog/2016/03/13/solving-sap-problems-without-reading-code--extract-a-famix-model-to-moose

 

The coding to extract and other informations are available on the github project page: https://github.com/RainerWinkler/Moose-FAMIX-SAP-Extractor

My CDS view self study tutorial - Part 9 cube view and query view

$
0
0

In previous eight steps all we focus on is transactional stuff. This time let's touch some analytics stuff.

 

Let's first create a most simple database table in ABAP backend:

clipboard1.png

Then create a simple cube view:

 

@EndUserText.label: 'Jerry cube view'
@Analytics.dataCategory: #CUBE
@VDM.viewType: #COMPOSITE
@AccessControl.authorizationCheck:#CHECK
@AbapCatalog.sqlViewName: 'zprdcube'
define view Z_C_Prod_Cube as select from zprd_query{   key zprd_query.prod_id,  zprd_query.prod_text,  @DefaultAggregation: #MAX  zprd_query.quantity
}

 

This cube view has only three fields: prod_id, prod_text and quantity.

For more detail for annotation @Analytics.dataCategory: #CUBE, please refer to SAP help.

Then create a query view on top of the cube view:

@EndUserText.label: 'Jerry query verification'
@VDM.viewType: #CONSUMPTION
@Analytics.query: true
@AccessControl.authorizationCheck:#NOT_ALLOWED
@AbapCatalog.sqlViewName: 'zprdquery'
@OData.publish: true
define view Z_C_Product as select from Z_C_Prod_Cube {  key Z_C_Prod_Cube.prod_id,  Z_C_Prod_Cube.prod_text,  @DefaultAggregation: #MAX  Z_C_Prod_Cube.quantity
}

Since I use @OData.publish: true, a new OData service is automatically generated when this query view is activated.

We have already discussed how this generation is achieved in this blog: My CDS view self study tutorial - Part 4 how does annotation @OData.publish work.

 

Once activation is finished, we can do some testing. In ABAP backend I have two entries in the table:

clipboard1.png

So once we perform the read operation via generated OData service: /sap/opu/odata/sap/Z_C_Product_cds/Z_C_PRODUCT

 

we can see these two entries are returned in OData response automatically:

clipboard2.png

Let's do some further research to find out how these two entries are retrieved from backend.

 

We have CDS view name as Z_C_Product and the generated OData service based on it has name Z_C_Product_CDS.

Like normal CDS view activation with annotation @OData.publish: true, there is also a new ABAP class @OData.publish: true automatically generated, which has only one method redefined: GET_QUERY_NAME. In this method, a constant attribute is returned.

The content of this attribute: '2Czprdquery'.

 

The biggest difference compared with a normal CDS view activation is: when a query view is activated, the generated OData service data provider class has CL_NAT_ODATA_MODEL_ABS as its super class, giving you a hint that all data retrieved based on this query view will be handled by analytics framework.

clipboard3.png


While for a normal CDS view, the DPC class has super class CL_SADL_GTK_EXPOSURE_MPC, which means the data access in this case is done by SADL framework.

clipboard4.png

In order to figure out the detail data access logic implementation by analytics framework, I write the following report to simulate the OData call:

 

REPORT zcds_get_query_view_data.
DATA(lo_tool) = NEW cl_nat_generic_dpc( ).
DATA(lo_context) = NEW /iwbep/cl_mgw_context( ).
DATA: l_r_rs_gw_columns TYPE REF TO cl_abap_tabledescr,      l_t_rs_gw_columns TYPE REF TO data,      lo_request        TYPE REF TO /iwbep/cl_mgw_request,      lo_detail         TYPE REF TO /iwbep/if_mgw_core_srv_runtime=>ty_s_mgw_request_context,      ls_detail         TYPE  /iwbep/if_mgw_core_srv_runtime=>ty_s_mgw_request_context,      lt_header         TYPE tihttpnvp,      lt_filter         TYPE /iwbep/t_mgw_select_option,      lt_order          TYPE /iwbep/t_mgw_sorting_order,      ls_page           TYPE /iwbep/s_mgw_paging,      ls_header         TYPE LINE OF tihttpnvp.
FIELD-SYMBOLS:    <l_t_rs_gw>       TYPE        table.
lo_context->/iwbep/if_mgw_context~set_parameter( iv_name  = /iwbep/if_mgw_context=>gc_param_isn     iv_value = 'Z_C_PRODUCT_CDS' ).
lo_context->/iwbep/if_mgw_context~set_parameter(  iv_name  = /iwbep/if_mgw_context=>gc_param_isv        iv_value = '0001' ).
lo_tool->/iwbep/if_mgw_core_srv_runtime~set_context( lo_context ).
CREATE DATA lo_detail.
lo_request = NEW /iwbep/cl_mgw_request( ir_request_details = lo_detail it_headers = lt_header ).
DATA(lo_rt) = NEW cl_eq_bics_gw_rt( i_query          = '2Czprdquery'                                    i_servicetype_oq = abap_true ).
lo_rt->get_designtime(  IMPORTING    e_t_column_description = DATA(l_t_query_struc) ) .
l_r_rs_gw_columns = cl_eq_bics_gw_dt=>build_rs_structure( l_t_query_struc ).
CREATE DATA l_t_rs_gw_columns TYPE HANDLE l_r_rs_gw_columns.
ASSIGN l_t_rs_gw_columns->* TO <l_t_rs_gw>.
ls_detail-technical_request-service_name = 'Z_C_PRODUCT_CDS'.
ls_detail-technical_request-service_version = '0001'.
ls_detail-technical_request-source_entity_type =  ls_detail-technical_request-target_entity_type
= 'Z_C_PRODUCTType'.
ls_detail-technical_request-source_entity_set = ls_detail-technical_request-target_entity_set
= 'Z_C_PRODUCTTypeCollection'.
ls_header-name = 'dummy'.
APPEND ls_header TO ls_detail-technical_request-request_header.
CALL METHOD lo_tool->/iwbep/if_mgw_core_srv_runtime~read_entityset(  EXPORTING    iv_entity_name           = 'Z_C_PRODUCTType'    iv_source_name           = 'Z_C_PRODUCTType'    is_paging                = ls_page    it_order                 = lt_order    it_filter_select_options = lt_filter    is_request_details       = ls_detail  CHANGING    cr_entityset             = l_t_rs_gw_columns    ct_headers               = lt_header                               ).
ASSIGN l_t_rs_gw_columns->* TO <l_t_rs_gw>.
WRITE: 'lines of data: ', lines( <l_t_rs_gw> ).

 

With SAT trace I can easily locate the exact location of code where the data retrieve is done:

clipboard5.png

clipboard6.png

In this line the DB cursor is opened with generated SQL statement:

clipboard7.png

clipboard8.png

Mystery Revealed!

clipboard9.png

How to filter ATC findings to detect only NEW findings

$
0
0

Did you ever think that it would be nice to have an ATC check variant that allows you to see only the new findings, that come from the changes that you just apply to existing “legacy” code?

 

This is especially useful for SAP customers, that have (or plan to have) ATC checks configured to run during transport release. In a typical SAP customer situation, when a developer performs some small bugfix on an existing, old Z-program, he (or she) does not want to correct all the old quality problems that that program has. And additionally, the business user does not want to test all functions of the program, when she (or he) asked only for one little bugfix or change in a specific part of the program.

 

Wouldn’t it be nice to have a filter that lets us see only those findings that did not already exist in previous versions of the program?

 

I have heard that SAP is working on such a feature for the ATC. But it happened that I stumbled over an enhancement spot where I could implement it myself relatively easy (there is one bigger problem for a specific case though – see at the end of this blog).

 

The suitable spot is an implicit enhancement in class CL_SATC_CI_ADAPTER, at the end of method if_Satc_Ci_Adapter~analyze_Objects in the local class.


In that position, I inserted a call to a new class:

 

ENHANCEMENT 1  ZS_ATC_FILTER_FINDINGS.    "active version    

     zcl_s_atc_filter_findings=>filter( exporting i_or_inspection = anonymous_inspection

                                      changing  c_it_findings = findings ).

ENDENHANCEMENT.

 

This is how the filter class is defined:

 

CLASS zcl_s_atc_filter_findings DEFINITION

  PUBLIC

  FINAL

  CREATE PUBLIC .

 

  PUBLIC SECTION.

    CLASS-METHODS:

      filter

        IMPORTING i_or_inspection    TYPE REF TO cl_ci_inspection

        CHANGING  c_it_findings      TYPE scit_rest.

  PROTECTED SECTION.

  PRIVATE SECTION.

    CLASS-METHODS:

      is_finding_new

        IMPORTING i_wa_f TYPE scir_rest

        RETURNING VALUE(r_result) TYPE abap_bool

        RAISING cx_satc_failure,

      init_comparison_data

        IMPORTINGi_it_findings TYPE scit_rest

        RAISINGcx_satc_failure,

      get_consolidated_names

        IMPORTING i_it_findings TYPE scit_rest

        RETURNING VALUE(r)      TYPE if_satc_result_access_filter=>ty_object_names,

      filter_previous_findings

        CHANGINGc_it_findings TYPE scit_rest

        RAISINGx_satc_failure.

 

    CLASS-DATA s_comparison_findings TYPE scit_rest.

ENDCLASS.


In the filter() method, I check the variant name. For Z_DELTA, I forward to method filter_previous_findings() to do the actual work.

 

  METHOD filter.

    CHECK c_it_findings IS NOT INITIAL.

    TRY.

        IF i_or_inspection->chkv->chkvinf-checkvname = 'Z_DELTA'.

          filter_previous_findings( CHANGING c_it_findings = c_it_findings ).

        ENDIF.

      CATCH cx_satc_failure INTO DATA(cx).

        DATA(exc_text) = cx->get_text( ).

        MESSAGE exc_text TYPE 'E'.

    ENDTRY.

  ENDMETHOD.


Method filter_previous_findings() does what its name says:


  METHOD filter_previous_findings.

    init_comparison_data( i_it_findings = c_it_findings ).

 

    LOOP AT c_it_findings ASSIGNING FIELD-SYMBOL(<f>).

      IF NOT is_finding_new( <f> ).

        DELETE c_it_findings USING KEY loop_key.

      ENDIF.

    ENDLOOP.

  ENDMETHOD.


But how can we compare with the check results of a previous version of the program? It is relatively easy, if we regularly run mass checks (for all customer coding) on the quality/test system, and replicate these checks to the development system. In this case, we can access those findings with the ATC API classes.

 

Method init_comparison_data() is the key element: it selects the newest complete, central check run from table SATC_AC_RESULTH, using a pattern for the title. You will have to adapt this pattern to your system name, or whatever you configured as name for the check run in your quality/test system.

 

  METHOD init_comparison_data.

    DATA(object_names) = get_consolidated_names( i_it_findings ).

    CHECK object_names IS NOT INITIAL.

    DATA(or_factory) = NEW cl_satc_api_factory( ).

    DATA(or_filter) = or_factory->create_result_access_filter( i_object_names = object_names ).

 

    SELECT display_id FROM satc_ac_resulth

      WHERE is_central_run = 'X'

        AND is_complete = 'X'

        AND title LIKE 'D1Q:%'    " adapt this to the pattern of your mass test run name

        ORDER BY scheduled_on_ts DESCENDING

      INTO @DATA(display_id)

        UP TO 1ROWS.

    ENDSELECT.

    CHECK sy-subrc = 0.

 

    or_factory->create_result_access( i_result_id = display_id )->get_findings(

                                         EXPORTING i_filter   = or_filter

                                         IMPORTING e_findings = s_comparison_findings ).

ENDMETHOD.


In the above method, we do not want to load all findings of the mass run (usually an enormously big number, depending on your system), so we prepare a filter from the objects in the current findings, using method get_consolidated_names().

 

  METHOD get_consolidated_names.

    DATA wa LIKE LINE OF r.

    wa = VALUE #( sign = 'I'option = 'EQ' ).

    LOOP AT i_it_findings ASSIGNING FIELD-SYMBOL(<f>).

      wa-low = <f>-objname.

      APPEND wa TO r.

    ENDLOOP.

    SORT r.

    DELETE ADJACENT DUPLICATES FROM r.

  ENDMETHOD.


And here is the method for the actual comparison:

 

  METHOD is_finding_new.

    READ TABLE s_comparison_findings WITH KEY test     = i_wa_f-test   " test class

                                              code     = i_wa_f-code

                                              objtype  = i_wa_f-objtype

                                              objname  = i_wa_f-objname

                                              " sub object (where the findings was actually detected)

                                              sobjtype = i_wa_f-sobjtype

                                              sobjname = i_wa_f-sobjname

                                              param1   = i_wa_f-param1  

                                              " param2 seems to contain technical and sometimes

                                              " language-dependent info, so we ignore it

                                    TRANSPORTING NO FIELDS.

    r_result = xsdboolsy-subrc<> 0 ).

  ENDMETHOD.


That’s it!

 

Unfortunately, there is one little loophole: if you use ATC as part of the transport release, and if you either grant limited exemptions for ATC findings (that expire at a certain date), or if you allow “emergency transports” to bypass the checks in some way, then you accumulate “new dirt” in your test/quality system, and you will never notice this because the mechanism proposed here cannot distinguish the “new dirt” from the old, “accepted” dirt.

 

A simple solution for this is to keep the comparison run from your quality/test system fixed, and not replace it with newer runs.

 

However, this has a disadvantage if you ever want to switch on additional checks in your check variant. In that case, all findings of the new checks will appear as “new”, even if they already existed in old coding.

 

To overcome this, we implemented a “dirt list” database table, where we store all unresolved findings that were transported to the quality/test system (for whatever reasons). If there is sufficient interest, I will explain this in another blog.


Printing DMS Documents

$
0
0

SAP Document Management (DMS) is one of the central functions in Logistics and cross-application component of SAP ECC. It supports many document management functions for SAP system as well as external systems. Please refer SAP help portal and SCN wiki for more details.


Several times there is requirement to print documents from DMS. One such scenario is printing Work Packets from Maintenance Order (IW32) through menu Order -> Print -> Order (or Operation selection). But the below discussion is not restricted to this scenario and generally applicable for all printing requirements of DMS documents.


These documents may have varying formats - PDF, Word, Excel, PPT, JPEG, etc. They may be stored on some third party system like SharePoint. Sending them to spool for printing for all these formats is not feasible. Here comes handy the standard SAP class supporting many front-end functions CL_GUI_FRONTEND_SERVICES.


If the documents are stored on an external system, you need to get the document as string of Hexadecimal characters (it may be proxy call to PI system, which was the scenario in my case). You can get the document into an internal table in binary format for subsequent processing:

 

               DATA: xbuffer TYPE xstring,

                          filelength TYPE i,

                          it_data TYPE TABLE OF tbl1024.

 

               CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'

                      EXPORTING

                        buffer        = xbuffer

                      IMPORTING

                        output_length = filelength

                      TABLES

                        binary_tab    = it_data.

 

If the document is stored locally, you may perhaps use F/M SDOK_PHIO_LOAD_CONTENT. I have not worked on this scenario though. Once you have the document in the internal table, you can download to the local machine (Concatenate the file name, you desire, to the file_name field below to make it a complete filename with path):

 

DATA: filelength TYPE i,

          file_name TYPE string VALUE 'C:\Temp',

           file_len TYPE i.

 

CALL METHOD cl_gui_frontend_services=>gui_download

                 EXPORTING

                   bin_filesize     = filelength

                   filename         = file_name

                   filetype         = 'BIN'

                 IMPORTING

                   filelength       = file_len

                 CHANGING

                   data_tab         = it_data

                 EXCEPTIONS

                   file_write_error = 1

                   OTHERS           = 2.

 

The bin_filesize parameter above is optional and seems to be innocuous but it's not. I missed to pass the file length to this parameter (I got from F/M SCMS_XSTRING_TO_BINARY) and to my annoyance I was unable to open the downloaded file (with message: The file is damaged) whatever I did. Somebody with good experience with binary files came to my rescue and suggested to pass this parameter and it worked after that. Lesson of the story is file length (in bytes) is very important when dealing with binary files.


Upon successful download, you can send it to printer by calling the class's EXECUTE method:

 

CALL METHOD cl_gui_frontend_services=>execute

                   EXPORTING

                     document               = file_name

                     operation              = 'PRINT'

                   EXCEPTIONS

                     .....

 

After printing the document successfully, you may want to delete it which can be handled by FILE_DELETE method of the class:

 

CALL METHOD cl_gui_frontend_services=>file_delete

                         EXPORTING

                           filename           = file_name

                         CHANGING

                           rc                 = rc

                         EXCEPTIONS

                           file_delete_failed = 1

                           OTHERS            = 9.

 

 

IF rc = 32.

     MESSAGE i461(iw) WITH 'File is open in another window. Please close doc '.

ENDIF.

 

So thats how this class handles all aspects of front-end handling of file automating download, print and delete functions. You may print several documents of different formats keeping the above logic in a LOOP. Enjoy !

Three ways to achieve conditional break point in your ABAP program

$
0
0

Let's use the simple program below.

 

Line 15 will be executed 1000 times. And we are only interested with a given iteration, for example we want to ONLY stop at line 15 with condition <data> = 22.

clipboard1.png

Approach1 - Source code breakpoint in ABAP debugger

 

Create a new breakpoint in debugger dynamically:

clipboard1.png


Maintain your condition as below:

clipboard2.png

And then you should see this new conditional break point set in line 15:

clipboard3.png

Then F8 to continue, the break point is triggered only once when <data> = 22.

clipboard4.png

Approach2 - Watchpoint

clipboard5.png


clipboard6.png

You should see your created watch point here:

clipboard7.png

Execute program, watch point is triggered:


clipboard8.png

clipboard9.png

Approach3 - ABAP debugger script

 

Create a new debugger script:

clipboard1.png

Click "Script Wizard"->"Variable Value(for Simple Variable)":

clipboard2.png

The wizard will generate code automatically for you ( marked with red ). You can finish the left code to achieve conditional break( marked with blank ). Save your script with a name.

clipboard3.png

Now launch your program, load the saved Script:


clipboard4.png

Then click Start Script:

clipboard5.png

Break point is triggered only once:

clipboard6.png

How to lock TMG only at Row level

$
0
0

I read somewhere

Walking on water and building code for requirements is very easy... As long as they are frozen .

 

We started with a requirement which needed a user to be able change the data in one of the custom tables .

 

Everything was tested and finalized for production when just a days from the Go live the business comes back and saying they need multiple users in in the TMG Transaction at the same time working on different sets of data.

 

We did not have the time to redesign the whole thing using editable ALV's.

Also I did not really want to go the ALV way as TMG has a more complete experience.

 

So that's when I decided to look into trying to make it lock at a record level instead of locking the complete table , with some help from the various other articles here's how we achieved it. ( thought it would be nice to document it for future use) .

 

  • Create the table and a Lock object , (this one had a big primary key) .

1.png

  • Create the TMG for it .

2.png

  • Make a note of the Function group used and the Screen number.

3.png

  • Navigate to the FG in SE80 .

4.png

 

  • Open the Screen and add a new Module( ZCUSTOM_ENQUE)  in the PBO section .

5.png

  • Put in the following code ( I prefer to create a  new include to store custom modules )
MODULE zcustom_enque OUTPUT.
*Call the function module corresponding to the lock object we created  CALL FUNCTION 'ENQUEUE_EZXXXXXXXXX'    EXPORTING      mode_ztpp_XXXXXX = 'E'      mandt            = sy-mandt      arbpl            = ztpp_xxxxxx-arbpl      matnr            = ztpp_xxxxxx-matnr      dtvon            = ztpp_xxxxxx-dtvon      werks            = ztpp_xxxxxx-werks      mdv01            = ztpp_xxxxxx-mdv01      srno             = ztpp_xxxxxx-srno      x_arbpl          = ' '      x_matnr          = ' '      x_dtvon          = ' '      x_werks          = ' '      x_mdv01          = ' '      x_srno           = ' '      _scope           = '2'      _wait            = ' '      _collect         = ' '    EXCEPTIONS      foreign_lock     = 1      system_failure   = 2      OTHERS           = 3.  IF sy-subrc <> 0.
* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
*         WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
* row is locked..hence gray..    LOOP AT SCREEN.      screen-input = 0.      MODIFY SCREEN.    ENDLOOP.  ENDIF.
ENDMODULE.                 " ZCUSTOM_ENQUE  OUTPUT 
  • Create a report program in SE38 ( this will be the entry point for the user )

7.png

  • The below Report ensures there are no locks at the table level when the TMG is called and should be the only call method for a TMG . ( assign a Tcode to this report ) .

 

REPORT  zpp_XXXXXXXXXXXXXXXX_tmg.
**Selection range for view maintenance
DATA:  BEGIN OF selekttab OCCURS 1.                INCLUDE STRUCTURE vimsellist.
DATA: END OF selekttab,
**Table of inactive CUA functions for view maintenance
BEGIN OF excl_cua_funct OCCURS 1.        INCLUDE STRUCTURE vimexclfun.
DATA: END OF excl_cua_funct.
DATA: lt_enq_del TYPE STANDARD TABLE OF seqg3,      lt_enq_read TYPE STANDARD TABLE OF seqg7,      lw_enq_read TYPE seqg7,      lw_enq_del TYPE seqg3,      lv_subrc TYPE sy-subrc.
*Read all the lock details in system
CALL FUNCTION 'ENQUE_READ2'  EXPORTING    gclient = sy-mandt    gname   = ' '    guname  = '*'  TABLES    enq     = lt_enq_read.
*We will search entry for table level lock for our table
LOOP AT lt_enq_read INTO lw_enq_read
 WHERE gname EQ 'RSTABLE'
 AND   garg CS 'ZTXXXXXX' .  " table name    MOVE-CORRESPONDING lw_enq_read TO lw_enq_del.  APPEND lw_enq_del TO lt_enq_del.
ENDLOOP.
*Delete table level lock entry for our table
CALL FUNCTION 'ENQUE_DELETE'  EXPORTING    check_upd_requests = 1  IMPORTING    subrc              = lv_subrc  TABLES    enq                = lt_enq_del.
*Now call the table maintenace generator.
CALL FUNCTION 'VIEW_MAINTENANCE_CALL'  EXPORTING    action               = 'U'    view_name            = 'ZTXXXXXXXX' " TMG name     show_selection_popup = space  TABLES    dba_sellist          = selekttab    excl_cua_funct       = excl_cua_funct.

Hope this helps someone.


Amarpreet.

CodeRetreat: How to perfect your Software-Craftsmanship as an ABAP Dev

$
0
0

What is a CodeRetreat?

Diverse People Friendship Togetherness Happiness Aerial View Concept

               Community Driven


You surely know this: In the small-small of every days programming you have little or no time to improve your skills and practice. You may participate in one or other training – but the improvements in the functioning and deepening existing knowledge remain mostly still on the tracks. This can be seen as a modern craftsmanship, however its perfections should be worked on a regularly basis.

„Retreats ” are certainly in trend. Many people would be thinking of a spiritual retreat as in a monastery. But we programmers also know the term: A code Retreat is an established, proven learning format in which you focus without having time pressure entirely on the programming and good code design. To improve the way you work and deepen your existing knowledge – all that, without thinking of your current project and its impending deadlines.

The Set-up of a CodeRetreat

A Code-Retreat usually takes place in one day. This will be divided in five to six 45 programming session. Between each session there is 15 minutes break for a review, a coffee break and a briefing of the next meeting. In the morning you will familiarize yourselves with the problem to break with old habits. In the afternoon it will become more demanding: We will make your abstraction and test your ability to test-driven development. Instead of gaining new insights, you spend the day to practice these concepts.

As a work equipment we generally use pair programming as the knowledge transfer is an essential part of this exercise. In each session you will work with another partner, select a part of the problem to be solved and begin the test-driven development. After each session you will delete the developed code (completely!) and exchange information with the other participants over the learned lessons.

To make matters even more interesting we use special handicaps which will be incorporated into the sessions, for example you are only allowed to communicate with your partner through code.

As an exercise we usually use “Conway’s Game of Life”. After a few sessions you will realize that you will never be able solve the problem and that you in face you should not solve it. It’s purely about the passion for clean code, good design and test-driven programming.

You will see: If you work together with others and have fun on the set tasks, the learning effect will automatically occur!

Personal Experience

I personally took part at different CodeRetreat´s. Often they are Programming-Language-Driven like a JavaScript- or Java- CodeRetreat.

But sometimes I´d have the possibility to explore different Programming-Languages and also to use ABAP for solving the Task.

Besides the advantages in Learning some new Programming Technics. The main advantage is to work on your Craftsmanship and to get in touch with the agile Community.

How is your experience?

- Do you have any experience with CodeRetreats?

- Or similar community-driven Events?

- Does it make a difference in using ABAP or another Mainstream Language?


Packed Number Simplified!

$
0
0

To write the first blog took me some time though there are few topics in my mind which I think I should blog. To write a blog with the right contents and presenting in a way which is very helpful for others is a challenge and hats off to al the bloggers for their efforts. I have been a constant follower of scn blogs and many such blogs has helped me a lot. So this is my first humble attempt to share something which I feel would help anyone who come across similar issue.

 

The topic is about packed number. Recently I came across an issue with one of our customers where they having issues storing large amount value to a packed decimal field. It was throwing overflow exception . To solve this issue I tried searching to get to know how packed decimals behave I couldn't get enough details anywhere about the exact behavior of packed decimals and i had spend some time to write a test program to understand the right behavior of packed decimals.

 

Customer was having  a custom domain of data type dec length  16 and decimal 9 .Based on the various documentations on packed number our assumption was that we have created a packed number with maximum length possible which is length 16 .

image - Copy.jpg

 

The amount value we were trying to store is 100000000.00. 9 digits before the decimal and 2 digits after the decimal .We were getting an overflow exception .I could see from the amount value if i reduce the number of digits before the decimal by one the overflow exception doesn't happen ( 8 digits before the decimal and 9 digits after the decimal ) . This made me wonder what's the length all about and how it works in case of packed decimal . I spend some time debugging the issue.

 

Below is how it appeared in the debugger .

debugger1.jpg

What I understand is  though I have declared my domain is of length 16 and dec 9 ,internally system converts into a packed decimal of length 9 and

dec 9 .P(9) DECIMAL 9.Now I had to understand what's this length stands for ,what does it mean by length 9  in case of packed decimal.

 

Predefined Numeric Types is explained here ABAP Keyword Documentation

 

Based on  definitions about packed decimal length 9 means 9 bytes and  2 digits forms 1 byte and so the actual length is 18 digits which includes one space for +/- sign. So in this case a domain  declared with the  length 16 and decimal 9 is converted by the system as a packed decimal of length 9 and decimal 9 which means a total of 18 digits including 9 decimal and 8 digits before the decimal and one space for the sign . Now I understood the reason why the amount 100000000.00 was not accepted by the variable ,instead 10000000.00 was accepted .

 

Overview of All Predefined Dictionary Types is explained here ABAP Keyword Documentation

 

Below is the test program to understand more about packed decimals.

 

 

REPORT ZTEST_PROGRAM

 

*Data Declarations

data lv_char(50) value '9.999999999'.

data: lv_p TYPE p LENGTH 16 DECIMALS 9.

data: lv_num type i value 1.

data: lv_dec type i value 9.




write:/ 'Keeping Decimal place constant as 9'.


DO 25 TIMES.

lv_num = lv_num + 1.

CONCATENATE '9' lv_char  into lv_char.

TRY .

  lv_p  = lv_char.

CATCH CX_SY_CONVERSION_OVERFLOW .

write:/'overflow'.

  ENDTRY.

CHECK lv_p is NOT INITIAL.

  write:/ lv_p, lv_num ,'Numbers and', lv_dec,' Decimals'.

ENDDO.

uline.



write:/ 'Keeping Decimal place constant as 9 in the situation'.

 

CLEAR: lv_num,lv_dec,lv_p,lv_char.

lv_char = '9.999999999'.

lv_num = 1. lv_dec = 9.


DO 10 TIMES.

  lv_num = lv_num + 1.

CONCATENATE '9' lv_char  into lv_char.

TRY .

  lv_p2  = lv_char.

CATCH CX_SY_CONVERSION_OVERFLOW .

  write:/'overflow'.

ENDTRY.

CHECK lv_p2 is NOT INITIAL.

  write:/ lv_p2, lv_num ,'Numbers and', lv_dec,' Decimals'.

ENDDO.

uline.


write:/ 'Keeping Decimal place constant as 3'.

CLEAR: lv_num,lv_dec,lv_p,lv_char.

data: lv_p1 TYPE p LENGTH 9 DECIMALS 3.

lv_char = '9.999'.

lv_num = 1.

lv_dec = 3.

DO 20 TIMES. lv_num = lv_num + 1.

CONCATENATE '9' lv_char  into lv_char.

TRY .

   lv_p1  = lv_char.

CATCH CX_SY_CONVERSION_OVERFLOW .

  write:/'overflow'.

ENDTRY.

  CHECK lv_p1 is NOT INITIAL.

  write:/ lv_p1, lv_num ,'Numbers and', lv_dec,' Decimals'.

ENDDO.



If we have to keep the length fixed for the  domain( declared length 16 decimal 9 ) , reduce the decimal points to hold more digits before the decimal .Below is a screenshot to show when the overflow will occur for the used domain reducing the decimal is reduced to 3 digits. Below is the output from the above code . This shows 14 Numbers are possible before decimal and 3 numbers after decimal.


decimal.png


So what we have to understand is the length defined in the domain is not the actual length of the packed decimal. if I declare a domain of length 20 and decimal 9 ,in debugger I could see the system converts it to a packed decimal of length 11 and decimal 9.The maximum allowed length of packed decimal is 16 which is 16 bytes .


So to understand maximum value possible, I have increased the local packed decimal variable to 16 and was able to see maximum number possible keeping decimals as 9 was huge number - in the help documentation - a formula is provided as well. For easy understanding below screen shot is given.

packed decimal max.png


So above code also support my understanding of 16*2 =  32 numbers possible in which 1 will be taken by +/- sign - which will be 22+9 = 31.


Hope this blog was useful.


You can also read more about predefined numeric type in in help.sap.com


Viewing all 943 articles
Browse latest View live