Quantcast
Channel: Scripting Languages
Viewing all 68 articles
Browse latest View live

PHP and MySQL?

$
0
0

Recently as I’ve dug more and more into HANA (how could I not with Tom and Rich publishing so many cool things?) I found that I could take many of my existing demos from PHP and MySQL and port them to HANA. The pain though has always been migrating DB content so I decided to make a simple little tool to make life easier.

It was during the SAP InnoJam event in Madrid that I sat down and coded something that I was able to test with Rich’s help, HOWEVER this is not something SAP supports or even validates or approves this is just me and my own HANA Cloud instance having some fun and deciding to share!

Screen-Shot-2012-11-24-at-14.02.07.png

GitHub – Move to HANA

I call it Move to HANA and you can find the code on GitHub. It’s fairly simple and I used JQuery Mobile(don’t hate me SAP UI5 lovers) to put it together, just upload it to your PHP server and run it.

The idea behind it, is to give a simple migration tool that will help generate the HANA specific SQL to get your same tables and fields generated and your data uploaded via the HANA Studio. Right now it’s setup for MySQL but is easily expanded for any DB you are working on; oh and of course it’s not 100% yet it’s only got a handful of the different field types but hey – it’s GitHub go ahead and make some changes!

As for execution – again very simple.

Screen-Shot-2012-11-24-at-14.02.51-1024x635.png

Database selection

 

Once your select your database then you will need to log into the database to select which “database” you want to migrate.

Screen-Shot-2012-11-24-at-14.06.43-1024x634.png

 

Login into your database

 

Then select your “database” at which point the script will generate the 3 step process for data migration.

Screen-Shot-2012-11-24-at-14.07.24-300x90.png

 

Simple 3 Steps

 

Step 1, will provide your the “copy and paste” script you need to enter into your HANA Studio.

Screen-Shot-2012-11-24-at-14.07.46.png

 

HANA SQL to generate SCHEMA

Step 2, will provide the command line code for MySQL to generate the CSV files you can then upload via the HANA Studio

Screen-Shot-2012-11-24-at-14.08.00.png

 

Command Line for MySQL Dumps

Step 3, will provide the SQL command to ensure the proper permissions for the newly generated SCHEMA

Screen-Shot-2012-11-24-at-14.08.56.png

 

SCHEMA permissions

I’ve used the script a dozen times already to get sample data and other MySQL data moved into HANA to allow myself more and more opportunities to try my hand at HANA coding and it’s worked quite well. If you give a try feedback would be great and if you find new data types or if you want to try a different database than MySQL by all means have at it!!

 

** SAP does not support nor endorse this tool and there is no support other than the very little free time that I personally have to answer comments. **


Automatizing tasks with Python

$
0
0

Hi everybody!

 

Since few weeks ago I am working in a project where I have to carry on some repetitive tasks on a SAP system (a Production clone).

 

First of these tasks is check if SAP is up and running (if not, I have to restart it) so I've developed a python script to automatize it and now I would like to share it with you.


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import subprocess
import shlex

CMD_SAP_CHECK = "startrfc -3 -u < USER > -p < PASSWORD > -c < CLIENT > -l < LANG > " \
                "-h < SAPMSGSERVER > -s < INST.NUM > -F SUBST_START_BATCHJOB " \
                " -E JOBNAME=CHECK_IF_SAP_IS_UP -E REPNAME=SAPMSM66"
CMD_SAP_START = "startsap < SAPMSGSERVER > all"
CMD_SAP_STOP = "stopsap < SAPMSGSERVER > all"

def execute(cmd):
    try:
        args = shlex.split(cmd)
        process = subprocess.Popen(args, stderr=subprocess.PIPE)
        process.wait()
        return process
    except Exception, error:
        print "ERROR: ", error
        raise


if __name__ == "__main__":
    process = execute(CMD_SAP_CHECK)
    if process.returncode != 0:
        log = open("checksap.log", "w")
        log.write("    RC: %d\n" % process.returncode)
        log.write("STDERR:\n%s" % process.stderr.read())
        log.close()
        execute(CMD_SAP_STOP)
        execute(CMD_SAP_START)

 

If the script is able to launch the background job it will return return code 0.

If not, it will return another number and it will write stderr output to checksap.log file:

    RC: 1
STDERR:
RFC Call/Exception: SYSTEM_FAILURE
Group       Error group 104
Key         RFC_ERROR_SYSTEM_FAILURE
Message     Name or password is incorrect (repeat logon)

Then, it will stop SAP system and after that, start it again.

 

 

Some notes:

  • It must be executed with <SID>ADM user
  • It was developed with Python 2.6 under AIX but it should work in any OS platform (I've checked it in Windows with Python 2.4 and it worked just changing startrfc path).
  • I used to launch another report instead of SAPMSM66 but after reading this post [1] I've started to use it.
  • This script was simplified for this post. My production script is part of a bigger development and it is more elaborated. Just consider it as an example.
  • This is my first post. Not sure if I should have been posted in my own SCN Blog or in this space. Advice please. Anyway, I hope it is useful for someone.

 

[1] SM66 - a small tip that can make a big difference

SAP Developer's Guide to PHP

$
0
0

Years ago I wrote the first book on SAP and PHP technologies together, it was published by SAP PRESS and was more of a success than we originally thought it would be. Now 8 years later I decided to take the content and republished a 2nd edition.

 

The book came about based on my entering into the SAP environment and all the blogs I wrote about that journey.

 

 

The new book is available in print, Kindle and iBook format and usually you can ask any questions you like here or here.

 

 

The updated version of the book includes new information about the RFC connectors as well as a section on SAP HANA.

BASIC Guide to Connect SAP

$
0
0

Hello community,

 

BASIC is a simple and powerful programming language. There are many very good BASIC dialects on the market, also as scripting versions. I wrote a book which describes various methods of connection different BASIC languages with an SAP system. It shows different aspects of connection, e.g. via SAP OCX modules and SAP NetWeaver RFC SDK. You will see, it is easy to build a bridge between the world of the SAP application server and your Desktop environment on the presentation server. The book is available as print version and as e-book, you can find it here. You find the homepage of the book here.

 

Cheers

Stefan

Calling R from the ERP - A dirty little hack

$
0
0

Since I joined SAP around 2 years ago, I simply stopped using ABAP…even when I use it for almost 11 years when I was a consultant…

 

A week ago, I was thinking about writing a new blog…something nice…some hacky…something that would allow me to just rest and don’t blog for the rest of the year…

 

I thought about ERP and R…while some things have been done already like Analytics with SAP and R from Piers Harding. I wanted to actually call R from the ERP instead of the ERP from R -;)

 

While thinking about this…I thought the best way to do it would be to reuse something that I before on my blog Consuming R from SAP Mobile Platform where I used Rook on Heroku to create a REST page that could be consumable…

 

So…for this particular blog…I wanted to something simple…call R, generate a graphic and show it on my ABAP program…here’s a small diagram to get things a little bit more clear…

 

ERP_Rook.jpg

 

Of course, when I tried this the first time…it failed…miserably…simply because the current implementation of Rook on Heroku doesn't have graphic capabilities on it…so it was impossible to actually generate a graphic…

 

Let’s get our hands dirty and let’s separate this blogs into steps so it become easier to handle

 

Compile R with graphic capabilities on Heroku

 

This step was really a pain…it took me almost a week to get it done…and funny enough the rest of the steps took me about 6 hours…anyway…let’s keep going…

 

I started by looking at this two awesome projects on Github…rookonheroku and heroku-buildpack-r-build-r I took some chips from both and then create my own script and steps…

 

If you don’t have Heroku Tool Belt installed…go ahead and grab it…Log into Heroku and do the following…

 

Steps to install R on Heroku with graphic capabilities

mkdir myproject && cd myproject

mkdir bin

echo "puts 'OK'" > config.ru

echo "source 'http://rubygems.org'\n gem 'rack'" > Gemfile

#Open your project folder and modify the Gemfile in Notepad…replace the “\n” by an actual line break.

bundle install

git init . && git add . && git commit -m "Init"

heroku apps:create myproject --stack=cedar

git push heroku master

#Copy and paste the content of my installR.sh file into your project folder, also copy the files features.h and features-orig.h from heroku-buildpack-r-build-r)

git add . && git commit -am "message" && git push heroku master

heroku ps:scale web=0

heroku run bash

cd bin/

./installR.sh

 

Here's the code for the installR.sh script...

 

installR.sh

#!/bin/bash

 

# NOTE: Heroku Cedar Stack

#  gcc needs to be 4.3

 

# See http://cran.r-project.org/doc/manuals/R-admin.html for details on building R

 

## HELPERS

 

function download() {

  if [ ! -f "$2" ]; then

    echo Downloading $2...

    curl $1 -o $2

  else

    echo Got $2...

  fi

}

 

function build() {

  echo ----------------------------------------------------------------------

  echo Building $@...

  echo ----------------------------------------------------------------------

  echo

  pushd $1

    ./configure --prefix=$vendordir/$2 ${@:3} && make && make install && make clean

  popd > /dev/null

  echo

  echo

 

  # add to libraries and pkg-config

  export LD_LIBRARY_PATH="$vendordir/$2:$LD_LIBRARY_PATH"

  export PKG_CONFIG_PATH="$vendordir/$2/lib/pkgconfig:$PKG_CONFIG_PATH"

 

}

 

## SCRIPT

 

set -e

 

r_version="${1:-2.13.1}"

r_version_major=${r_version:0:1}

 

if [ -z "$r_version" ]; then

  echo "USAGE: $0 VERSION"

  exit 1

fi

 

basedir="$( cd -P "$( dirname "$0" )" && pwd )"

 

# create output directory

vendordir=/app/vendor

mkdir -p $vendordir

 

echo ======================================================================

echo Downloading and unpacking dependancies...

echo ======================================================================

 

# We need to install xz to be able to unzip the gcc package we're going to download in a minute

curl http://gfortran.com/download/x86_64/xz.tar.gz -o xz.tar.gz

tar xzvf xz.tar.gz

 

 

# Get and unpack gcc-4.3 binary, including gfortran

curl http://gfortran.com/download/x86_64/snapshots/gcc-4.3.tar.xz -o gcc-4.3.tar.xz

./usr/bin/unxz gcc-4.3.tar.xz

tar xvf gcc-4.3.tar

 

# http://www.freetype.org/freetype2/

freetype_version=2.5.0

download http://www.mirrorservice.org/sites/download.savannah.gnu.org/releases/freetype/freetype-$freetype_version.tar.gz freetype-$freetype_version.tar.gz

tar xzf freetype-$freetype_version.tar.gz

 

# http://directfb.org/

directfb_version=1.2.9

directfb_ver_major=${directfb_version:0:3}

download http://directfb.org/downloads/Core/DirectFB-$directfb_ver_major/DirectFB-$directfb_version.tar.gz DirectFB-$directfb_version.tar.gz

tar xzf DirectFB-$directfb_version.tar.gz

 

# http://www.libpng.org/pub/png/libpng.html

libpng_version=1.2.50

download ftp://ftp.simplesystems.org/pub/libpng/png/src/libpng12/libpng-$libpng_version.tar.gz libpng-$libpng_version.tar.gz

tar xzf libpng-$libpng_version.tar.gz

 

# http://www.cairographics.org

pixman_version=0.29.4

download http://www.cairographics.org/snapshots/pixman-$pixman_version.tar.gz pixman-$pixman_version.tar.gz

tar xzf $basedir/pixman-$pixman_version.tar.gz

 

# http://www.freedesktop.org/software/fontconfig

fontconfig_version=2.9.0

download http://www.freedesktop.org/software/fontconfig/release/fontconfig-$fontconfig_version.tar.gz fontconfig-$fontconfig_version.tar.gz

tar xzf $basedir/fontconfig-$fontconfig_version.tar.gz

 

# http://www.cairographics.org

cairo_version=1.9.14

download http://www.cairographics.org/snapshots/cairo-$cairo_version.tar.gz cairo-$cairo_version.tar.gz

tar xzf $basedir/cairo-$cairo_version.tar.gz

 

# http://www.pango.org

#pango_ver=1.35

#pango_version=$pango_ver.0

#download http://ftp.gnome.org/pub/GNOME/sources/pango/$pango_ver/pango-$pango_version.tar.xz pango-$pango_version.tar.xz

#tar xJf $basedir/pango-$pango_version.tar.xz

 

# R

download http://cran.r-project.org/src/base/R-$r_version_major/R-$r_version.tar.gz R-$r_version.tar.gz

tar xzf R-$r_version.tar.gz

 

# http://gcc.gnu.org/wiki/GFortran

gcc_version=4.3

download http://gfortran.com/download/x86_64/snapshots/gcc-$gcc_version.tar.xz gcc-$gcc_version.tar.xz

tar xJf $basedir/gcc-$gcc_version.tar.xz -C $vendordir

 

# patch gcc features.h file

# see http://permalink.gmane.org/gmane.comp.gcc.help/40166

mkdir -p $vendordir/gcc-$gcc_version/lib/gcc/x86_64-unknown-linux-gnu/$gcc_version/include-fixed

cp $basedir/features.h $vendordir/gcc-$gcc_version/lib/gcc/x86_64-unknown-linux-gnu/$gcc_version/include-fixed/features.h

 

# https://www.gnu.org/software/libc/

glibc_version=2.7

glibc_version_x=2.7ds1

download ftp://ftp.gunadarma.ac.id/linux/debian/pool/main/g/glibc/glibc_2.7.orig.tar.gz glibc_$glibc_version.tar.gz

tar xzf $basedir/glibc_$glibc_version.tar.gz -C $vendordir

tar xjf $vendordir/glibc-$glibc_version/glibc-$glibc_version_x.tar.bz2 -C $vendordir

 

echo ============================================================

echo Building dependencies...

echo ======================================================================

 

build "$basedir/freetype-$freetype_version" freetype

build "$basedir/DirectFB-$directfb_version" DirectFB

build "$basedir/libpng-$libpng_version" libpng

build "$basedir/pixman-$pixman_version" pixman

build "$basedir/fontconfig-$fontconfig_version" fontconfig

build "$basedir/cairo-$cairo_version" cairo

 

# copy over missing header files

cp $basedir/cairo-$cairo_version/src/*.h $vendordir/cairo/include

 

#build $basedir/pango-$pango_version pango

 

# build R

echo ============================================================

echo Building R

echo ============================================================

cd $basedir/R-$r_version/

 

export LDFLAGS="-L$vendordir/gcc-$gcc_version/lib64"

export CPPFLAGS="-I$vendordir/glibc-$glibc_version/string/ -I$vendordir/glibc-$glibc_version/time"

export PATH="$vendordir/gcc-$gcc_version/bin:$PATH"

 

echo ----------------------------------------------------------------------

echo LD_LIBRARY_PATH=$LD_LIBRARY_PATH

echo PKG_CONFIG_PATH=$PKG_CONFIG_PATH

echo ----------------------------------------------------------------------

 

./configure --prefix=$vendordir/R --enable-R-shlib --with-readline=no --with-x=yes

make

 

cd /app/bin

#ln -s R-2.15-1/bin/R

ln -s R-2.13.1/bin/R

 

rm gcc-4.3.tar

rm glibc_2.7.orig.tar.gz

rm R.tar.gz

rm xz.tar.gz

rm -rf usr/

rm -rf gcc-4.3/bin

rm -rf gcc-4.3/lib

rm -rf gcc-4.3/libexec

rm -rf gcc-4.3/info

rm -rf gcc-4.3/man

rm -rf gcc-4.3/share

rm -rf gcc-4.3/include

 

rm glibc-2.7/*.tar.bz

cd bin/glibc-2.7/

rm -rf abilist/ abi-tags aclocal.m4  argp assert/ b* BUGS  C* c* d* e* F* g* h* i* I* l* m* M* N* aout/ LICENSES  n* o* p* P* R* r*

rm -rf scripts/ setjmp/ shadow/ shlib-versions signal/ socket/ soft-fp/ stdio-common/ stdlib/ streams/ sunrpc/ sysdeps/ sysvipc/

rm -rf termios/ test-skeleton.c timezone tls.make.c version.h Versions.def wcsmbs/ wctype/ WUR-REPORT

 

cd ../R-2.13.1

rm -rf src

rm Make*

rm -rf doc

rm NEWS*

rm -rf test

rm config*

rm O* README ChangeLog COPYING INSTALL SVN-REVISION VERSION

NEED etc

 

With that…you will have a full R installation with graphic capabilities…of course…you will need to delete all the garbage generated…but we will get there later…

 

Now…we need to install Rook…but we need version 1.0-2 so we need to download it and install it from the source…

 

Download Rook 1.0-2
Wget http://cran.r-project.org/src/contrib/Archive/Rook/Rook_1.0-2.tar.gz -o Rook_1.0-2.tar.gz

 

Then launch R…simply call it by saying…

 

Calling R
R

 

As it will launch the R environment…

 

Installing Rook

install.packages(/Rook_1.0-2.tar.gz, repos = NULL, type="source")

#Now we can simply quit R

q()

 

As I was lazy to work more on the script to delete those files automatically…basically…the “bin” folder should look like this…

 

Bin_Folder.jpg

 

And the “vendor” folder like this…

 

Vendor_Folder.jpg

 

Please remove the folders by using…

 

Removing folders

rm –rf name_of_folder

 

Once we’re done…we need to pack our stuff and sent it somewhere else, as Heroku is read only…once we log off…we lost everything…

 

Backup your work

tar -cvzf bin.tar.gz bin

tar -cvzf vendor.tar.gz vendor

scp bin.tar.gz me@myserver.com:/myproject/bin.tar.gz

scp vendor.tar.gz me@myserver.com:/myproject/vendor.tar.gz

 

#For scp you actually need a server…and I had my R on AWS…so I did used that…it might look like this…

scp -i XXX_X.pem bin.tar.gz root@XX.XX.XXX.XXX:/Blag/bin.tar.gz

 

Once we have everything saved…we can close the Heroku session and keep going…we can use WinSCP to copy our packed folders from our AWS server into our local project folder…uncompressed them and copy the following files from rookonheroku…

 

  • config.ru
  • demo.R
  • Procfile

 

Then…you can simply send everything back to Heroku…

 

Last push on Heroku

git add . && git commit -am "message" && git push heroku master

heroku ps:scale web=1

 

Of course…this is a lot of work…and I mean it…lot

 

So…I have upload the final compiled version to GitHub To get it…simply do this…

 

Getting Blagrook

git clone git:// github.com/atejada/BlagRook.git yourproject #(This will clone my Github and create a folder called yourproject to store the codes)

heroku create myapp #(Create an application for the Rook script)

git push heroku master #(This allows to pass everything from my yourproject folder to your Heroku account)

 

Create the code for the Rook application

 

On last thing we need to do…is to create the Rook application that it’s going to work on our Rook server…

 

demo.R

library(Rook)

 

newapp<-function(env){

   req<-Rook::Request$new(env)

   res<-Rook::Response$new()

 

   carrid_param<-c(req$params()$carrid)

   seats_param<-c(req$params()$seats)

 

   carrid_param<-strsplit(carrid_param,",")

   carrid_param<-c(carrid_param[[1]][1],carrid_param[[1]][2], carrid_param[[1]][3])

   seats_param<-strsplit(seats_param,",")

   seats_param<-c(as.numeric(seats_param[[1]][1]),as.numeric(seats_param[[1]][2]),

                            as.numeric(seats_param[[1]][3]))

 

   params<-data.frame(carrid_param,seats_param)

 

   bmp("R_Plot.bmp",type=c("cairo"))

   dotchart(params$seats_param,labels=params$carrid_param,xlab="Number of seats",ylab="Carriers")

   dev.off()

 

   to.read = file("R_Plot.bmp", "rb")

   x<-readBin(to.read, raw(),n=231488)

   hex<-paste(x, collapse = "")

 

   res$write(hex)

   res$finish()

}

 

 

server = Rhttpd$new()

server$add(app = newapp, name = "summarize")

server$start(listen="0.0.0.0", port=as.numeric(Sys.getenv("PORT")))

 

while(T) {

  Sys.sleep(10000)

}

 

In a few words what is happening here is simple...we receive the parameters "carrid" and "seats"...both came as a string with "," so we use strsplit to separate them and then create a factor with the values. In the case of "seats" we need it to be numeric so we use the as.numeric function. Finally we create a data.frame combining "carrid" and "seats". Using "Cairo" (That's why we needed graphic capabilities on R) we create a Bitmap file which is going to be the result of creating a dotchart. Once it has been created, we read it, using the readBin function we get it's Hexadecimal representation. Using the function paste and its collapse method, we put everything on a big string and send it back...easy as pie

 

I’m sure that you will wonder how I came out with the number 231488 on the readBin line…well…I download a nice Hexadecimal Editor called HxD then I create the graphic on my local R and load it…the I went to the last line and took the Hex number…

 

Hex_Values.jpg

 

Using the calculator I simply changed the Hex value 38830 to decimal, multiply it by 16 and then add 16 more as the first line doesn’t count…simply and easy

 

Create the function module ZUPLOAD_GRAPHICS_TABLE

 

This function module will allow us to upload the generated graphic into SE78.

 

ZUPLOAD_GRAPHICS_TABLE

FUNCTION ZUPLOAD_GRAPHICS_TABLE.

*"----------------------------------------------------------------------

*"*"Local Interface:

*"  IMPORTING

*"     REFERENCE(OBJECT) TYPE  TDOBJECT DEFAULT 'GRAPHICS'

*"     REFERENCE(NAME) TYPE  TDOBNAME

*"     REFERENCE(BTYPE) TYPE  TDBTYPE

*"     REFERENCE(ID) TYPE  TDID DEFAULT 'BMAP'

*"     REFERENCE(RESOLUTION) TYPE  TDRESIDENT OPTIONAL

*"     REFERENCE(RESIDENT) TYPE  TDRESIDENT OPTIONAL

*"     REFERENCE(AUTOHEIGHT) TYPE  TDAUTOHGHT OPTIONAL

*"  TABLES

*"      T_BITMAP OPTIONAL

*"----------------------------------------------------------------------

 

   TYPES: TY_BOOLEAN(1) TYPE C.

 

   TYPES: BEGIN OF TY_SBDST_CONTENT.

           INCLUDE STRUCTURE BAPICONTEN.

   TYPES: END OF TY_SBDST_CONTENT.

 

   TYPES: SBDST_COMPONENTS LIKE BAPICOMPON OCCURS 1,

          SBDST_PROPERTIES LIKE BAPIPROPER OCCURS 1,

          SBDST_SIGNATURE LIKE BAPISIGNAT OCCURS 1,

          SBDST_CONTENT LIKE BAPICONTEN OCCURS 1.

 

   DATA: T_BDS_CONTENT TYPE STANDARD TABLE OF

         TY_SBDST_CONTENT,

         WA_BDS_COMPONENTS TYPE LINE OF SBDST_COMPONENTS,

         L_BDS_PROPERTIES  TYPE SBDST_PROPERTIES,

         L_BDS_COMPONENTS  TYPE SBDST_COMPONENTS,

         T_STXBITMAPS TYPE STANDARD TABLE OF STXBITMAPS

         WITH HEADER LINE,

         WA_BDS_SIGNATURE TYPE LINE OF SBDST_SIGNATURE,

         L_BDS_SIGNATURE TYPE SBDST_SIGNATURE,

         L_BDS_CONTENT TYPE SBDST_CONTENT,

         WA_STXBITMAPS TYPE STXBITMAPS,

         WA_BDS_PROPERTIES TYPE LINE OF SBDST_PROPERTIES.

 

   DATA: W_BYTECOUNT TYPE I,

         W_COLOR(1) TYPE C,

         W_WIDTH_TW TYPE STXBITMAPS-WIDTHTW,

         W_HEIGHT_TW TYPE STXBITMAPS-HEIGHTTW,

         W_WIDTH_PIX TYPE STXBITMAPS-WIDTHPIX,

         W_HEIGHT_PIX TYPE STXBITMAPS-HEIGHTPIX,

         W_RESOLUTION TYPE STXBITMAPS-RESOLUTION,

         W_BDS_BYTECOUNT TYPE I,

         W_TYPE TYPE STXBITMAPS-TDBTYPE,

         W_DOCID TYPE STXBITMAPS-DOCID.

 

   DATA: L_BDS_OBJECT TYPE REF TO CL_BDS_DOCUMENT_SET.

 

   CONSTANTS:

     C_TRUE  TYPE TY_BOOLEAN VALUE 'X',

     C_FALSE TYPE TY_BOOLEAN VALUE SPACE,

     C_BDS_MIMETYPE  TYPE BDS_MIMETP VALUE 'application/octet-stream',

     C_BDS_CLASSNAME TYPE SBDST_CLASSNAME VALUE 'DEVC_STXD_BITMAP',

     C_BDS_CLASSTYPE TYPE SBDST_CLASSTYPE VALUE 'OT'.

 

   IF BTYPE = 'BMON'.

     W_COLOR = C_FALSE.

   ELSE.

     W_COLOR = C_TRUE.

   ENDIF.

 

   CALL FUNCTION 'SAPSCRIPT_CONVERT_BITMAP_BDS'

     EXPORTING

       COLOR                    = W_COLOR

       FORMAT                   = 'BMP'

       BITMAP_BYTECOUNT         = W_BYTECOUNT

       compress_bitmap          = 'X'

     IMPORTING

       WIDTH_TW                 = W_WIDTH_TW

       HEIGHT_TW                = W_HEIGHT_TW

       WIDTH_PIX                = W_WIDTH_PIX

       HEIGHT_PIX               = W_HEIGHT_PIX

       DPI                      = W_RESOLUTION

       BDS_BYTECOUNT            = W_BDS_BYTECOUNT

     TABLES

       BITMAP_FILE              = T_BITMAP

       BITMAP_FILE_BDS          = T_BDS_CONTENT

     EXCEPTIONS

       FORMAT_NOT_SUPPORTED     = 1

       NO_BMP_FILE              = 2

       BMPERR_INVALID_FORMAT    = 3

       BMPERR_NO_COLORTABLE     = 4

       BMPERR_UNSUP_COMPRESSION = 5

       BMPERR_CORRUPT_RLE_DATA  = 6

       OTHERS                   = 7.

 

   CREATE OBJECT L_BDS_OBJECT.

 

   WA_BDS_COMPONENTS-DOC_COUNT  = '1'.

   WA_BDS_COMPONENTS-COMP_COUNT = '1'.

   WA_BDS_COMPONENTS-MIMETYPE   = C_BDS_MIMETYPE.

   WA_BDS_COMPONENTS-COMP_SIZE  = W_BYTECOUNT.

   APPEND WA_BDS_COMPONENTS TO L_BDS_COMPONENTS.

 

   W_TYPE = 'BSD'.

 

   SELECT SINGLE *

   INTO T_STXBITMAPS

   FROM STXBITMAPS WHERE TDOBJECT = OBJECT

                                   AND   TDNAME   = NAME

                                   AND   TDID     = ID

                                   AND   TDBTYPE  = BTYPE.

   IF SY-SUBRC = 0.

     READ TABLE T_STXBITMAPS INDEX 1.

     W_DOCID = T_STXBITMAPS-DOCID.

   ELSE.

     CLEAR W_DOCID.

   ENDIF.

 

   IF W_DOCID IS INITIAL.

     WA_BDS_SIGNATURE-DOC_COUNT = '1'.

     APPEND WA_BDS_SIGNATURE TO L_BDS_SIGNATURE.

 

     CALL METHOD L_BDS_OBJECT->CREATE_WITH_TABLE

       EXPORTING

         CLASSNAME  = C_BDS_CLASSNAME

         CLASSTYPE  = C_BDS_CLASSTYPE

         COMPONENTS = L_BDS_COMPONENTS

         CONTENT    = T_BDS_CONTENT

       CHANGING

         SIGNATURE  = L_BDS_SIGNATURE

       EXCEPTIONS

         OTHERS     = 1.

 

     READ TABLE L_BDS_SIGNATURE INDEX 1 INTO WA_BDS_SIGNATURE

     TRANSPORTING DOC_ID.

     IF SY-SUBRC = 0.

       W_DOCID = WA_BDS_SIGNATURE-DOC_ID.

     ENDIF.

   ELSE.

     CALL METHOD L_BDS_OBJECT->UPDATE_WITH_TABLE

       EXPORTING

         CLASSNAME     = C_BDS_CLASSNAME

         CLASSTYPE     = C_BDS_CLASSTYPE

         DOC_ID        = W_DOCID

         DOC_VER_NO    = '1'

         DOC_VAR_ID    = '1'

       CHANGING

         COMPONENTS    = L_BDS_COMPONENTS

         CONTENT       = T_BDS_CONTENT

       EXCEPTIONS

         NOTHING_FOUND = 1

         OTHERS        = 2.

   ENDIF.

 

   WA_STXBITMAPS-TDNAME     = NAME.

   WA_STXBITMAPS-TDOBJECT   = OBJECT.

   WA_STXBITMAPS-TDID       = ID.

   WA_STXBITMAPS-TDBTYPE    = BTYPE.

   WA_STXBITMAPS-DOCID      = W_DOCID.

   WA_STXBITMAPS-WIDTHPIX   = W_WIDTH_PIX.

   WA_STXBITMAPS-HEIGHTPIX  = W_HEIGHT_PIX.

   WA_STXBITMAPS-WIDTHTW    = W_WIDTH_TW.

   WA_STXBITMAPS-HEIGHTTW   = W_HEIGHT_TW.

   WA_STXBITMAPS-RESOLUTION = RESOLUTION.

   WA_STXBITMAPS-RESIDENT   = RESIDENT.

   WA_STXBITMAPS-AUTOHEIGHT = AUTOHEIGHT.

   WA_STXBITMAPS-BMCOMP     = 'X'.

   INSERT INTO STXBITMAPS VALUES WA_STXBITMAPS.

 

ENDFUNCTION.


Create the ABAP program to make it all happen…

 

ZROOK

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

*& Report  ZROOK                                             &*

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

*& Author: Alvaro "Blag" Tejada Galindo               &*

*& Company: SAP Labs Palo Alto                       &*

*& Date: November 11, 2013                               &*

*& Modified on: November 18, 2013                     &*

*& Reason: Finish the code -;)                            &*

*&*******************************************************&*

 

REPORT  ZROOK NO STANDARD PAGE HEADING.

 

TYPES : BEGIN OF ty_bitmap,

         l(64) TYPE x,

         END OF ty_bitmap.

 

TYPES: BEGIN OF TY_GRAPHIC_TABLE,

        LINE(255) TYPE X,

        END OF TY_GRAPHIC_TABLE.

 

types: begin of ty_flights,

        carrid type sflight-carrid,

        seatsocc type sflights-seatsocc,

        end of ty_flights.

 

DATA: it_bitmap TYPE STANDARD TABLE OF ty_bitmap,

       i_po_data TYPE STANDARD TABLE OF string,

       i_bitmap TYPE STANDARD TABLE OF string,

       T_GRAPHIC_TABLE TYPE STANDARD TABLE OF TY_GRAPHIC_TABLE,

       t_flights type STANDARD TABLE OF ty_flights,

       bitmap type string,

       client TYPE REF TO if_http_client,

       container TYPE REF TO cl_gui_custom_container,

       lv_length   TYPE i,

       lv_content  TYPE xstring,

       lr_mime_rep TYPE REF TO if_mr_api,

       OUTXSTRING type xstring,

       URL(255) TYPE C,

       PICTURE TYPE REF TO CL_GUI_PICTURE,

       L_BYTECOUNT TYPE I,

       L_CONTENT TYPE STANDARD TABLE OF BAPICONTEN,

       GRAPHIC_SIZE TYPE I,

       bin TYPE xstring,

       conv TYPE REF TO cl_abap_conv_in_ce,

       response TYPE string,

       carrid type string,

       seats type string,

       seatsocc type string,

       param type string.

 

FIELD-SYMBOLS: <fs_bitmap> like line of i_bitmap,

                <fs_itbitmap> like line of it_bitmap,

                <fs_po_data> like line of i_po_data,

                <fs_flight> like line of t_flights.

 

START-OF-SELECTION.

   perform get_data.

   perform call_rook.

   perform create_graphic.

   perform show_graphic.

 

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

*&      Form  GET_DATA

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

FORM GET_DATA.

 

   select carrid sum( seatsocc ) into table t_flights

   from sflights up to 3 rows

   group by carrid.

 

   loop at t_flights ASSIGNING <fs_flight>.

     seatsocc = <fs_flight>-seatsocc.

     CONDENSE seatsocc NO-GAPS.

     CONCATENATE carrid <fs_flight>-carrid into carrid SEPARATED BY ','.

     CONCATENATE seats seatsocc into seats SEPARATED BY ','.

   ENDLOOP.

 

   replace regex '\A,' in carrid with space.

   replace regex '\A,' in seats with space.

 

   CONCATENATE '/custom/summarize?carrid=''' carrid '''&seats=' seats into param.

 

ENDFORM.                    " GET_DATA

 

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

*&      Form  CALL_ROOK

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

FORM CALL_ROOK.

 

   CALL METHOD cl_http_client=>create

     EXPORTING

       host   = 'blagrook.herokuapp.com'

       scheme = 1

     IMPORTING

       client = client.

 

   client->request->set_method( if_http_request=>co_request_method_get ).

   cl_http_utility=>set_request_uri( request = client->request uri = param ).

 

   client->send( ).

   client->receive( ).

 

   bin = client->response->get_data( ).

 

   conv = cl_abap_conv_in_ce=>create( input = bin ).

   conv->read( IMPORTING data = response ).

 

   client->close( ).

 

ENDFORM.                    " CALL_ROOK

 

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

*&      Form  CREATE_GRAPHIC

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

FORM CREATE_GRAPHIC.

 

   bitmap = response.

   TRANSLATE bitmap TO UPPER CASE.

 

   CALL FUNCTION 'SOTR_SERV_STRING_TO_TABLE'

     EXPORTING

       TEXT        = bitmap

       LINE_LENGTH = 128

     TABLES

       TEXT_TAB    = i_bitmap.

 

   loop at i_bitmap ASSIGNING <fs_bitmap>.

     APPEND INITIAL LINE TO it_bitmap ASSIGNING <fs_itbitmap>.

     <fs_itbitmap>-l = <fs_bitmap>.

   ENDLOOP.

 

   CALL FUNCTION 'ZUPLOAD_GRAPHICS_TABLE'

     EXPORTING

       OBJECT   = 'GRAPHICS'

       NAME     = 'BLAG'

       BTYPE    = 'BMON'

       ID       = 'BMAP'

     TABLES

       T_BITMAP = it_bitmap.

 

ENDFORM.                    " CREATE_GRAPHIC

 

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

*&      Form  SHOW_GRAPHIC

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

FORM SHOW_GRAPHIC.

 

   CALL FUNCTION 'SAPSCRIPT_GET_GRAPHIC_BDS'

     EXPORTING

       I_OBJECT       = 'GRAPHICS'

       I_NAME         = 'BLAG'

       I_ID           = 'BMAP'

       I_BTYPE        = 'BMON'

     IMPORTING

       E_BYTECOUNT    = L_BYTECOUNT

     TABLES

       CONTENT        = L_CONTENT

     EXCEPTIONS

       NOT_FOUND      = 1

       BDS_GET_FAILED = 2

       BDS_NO_CONTENT = 3

       OTHERS         = 4.

 

   CALL FUNCTION 'SAPSCRIPT_CONVERT_BITMAP'

     EXPORTING

       OLD_FORMAT               = 'BDS'

       NEW_FORMAT               = 'BMP'

       BITMAP_FILE_BYTECOUNT_IN = L_BYTECOUNT

     IMPORTING

       BITMAP_FILE_BYTECOUNT    = GRAPHIC_SIZE

     TABLES

       BDS_BITMAP_FILE          = L_CONTENT

       BITMAP_FILE              = T_GRAPHIC_TABLE

     EXCEPTIONS

       OTHERS                   = 1.

 

   CALL FUNCTION 'DP_CREATE_URL'

     EXPORTING

       TYPE    = 'IMAGE'

       SUBTYPE = 'BMP'

     TABLES

       DATA    = T_GRAPHIC_TABLE

     CHANGING

       URL     = URL.

 

   CREATE OBJECT container

     EXPORTING

       container_name = ''

       repid          = 'SAPMSSY0'

       dynnr          = '0120'.

 

   container->set_top( -85 ).

   container->set_left( 120 ).

   container->set_width( 580 ).

   container->set_height( 580 ).

 

   CREATE OBJECT PICTURE

     EXPORTING

       PARENT = container.

 

   CALL METHOD PICTURE->LOAD_PICTURE_FROM_URL

     EXPORTING

       URL = URL.

   CALL METHOD PICTURE->SET_DISPLAY_MODE

     EXPORTING

       DISPLAY_MODE = PICTURE->DISPLAY_MODE_FIT_CENTER.

 

   WRITE ''.

 

ENDFORM.                    " SHOW_GRAPHIC


When we run our program…we will call our Rook application on Heroku, which will receive the sum of seats for three carriers and then create a graphic, read it’s hexadecimal representation, give back to ABAP which will in turn create it on SE78 and then read it to show it on screen…now…you will not get the best resolution ever…and will assume it’s because “Cairo” doesn’t really support bmp files, or because the graphic is getting transported and changed all the way…so…it doesn't look good…but heck! We’re calling R from the ERP…what else do you want?

 

ERP_Rook_Graphic.jpg

It was really fun…so see you next year -;)

 

Blag.

Developer Experience.

Python for Basis (Part 1)

$
0
0

There are a few blog posts about Python that show how easy it is. Many of them are more from a developer view in my opinion so many basis people may not even consider reading through them. Consequently they will be missing out on a great opportunity to make their lifes easier and their work more efficient.  I hope you enjoy this first part. There will be more depending on the time I have.

The Teaser

Scripting repetitive activities like a system copy or SAP refresh validation or other activities makes perfect sense. The problem is that many of the scripts developed over the years are not very reusable. One customer runs on Unix/Oracle, the next Windows/MSSQL and another perhaps DB2.

 

Ever since I came across Python I am simply blown away by how simple it is to create complex scripts even including GUIs and SAP integration. One of the refresh scripts I developed at one of my clients was written in VB script for Windows and SQL Server. The script was huge, complicated and very difficult to maintain for the "uninitiated". The same functionality in Python would have been possible with a script less than 1/10th the number of lines. On top of that it would have been platform and even database independent! It would have also taken only a fraction of the time I required for the VBS script and would have been maintainable by almost to everybody.

 

For example these lines create a connection object to logon to SAP:

 

from pyrfc import Connection

conn = Connection(user=<username>,

                  passwd=<password>,

                  ashost=<hostname>,

                  sysnr=<system number>,

                  client=<client>)

 

Two commands and that's it. From that point on, "conn" will be the name of the SAP connection object that can be used to interact with the SAP instance and allows you to do many really cool things. For example schedule a program as background job:

 

result_open = conn.call('SUBST_SCHEDULE_BATCHJOB',

                         JOBNAME=<jobname>,

                         REPNAME=<program>,

                         VARNAME=<variant>,

                         AUTHCKNAM=<username>,

                         SDLSTRTTM=datetime.time(<hour>,<minute>,<second>),

                         STRTIMMED='X')

 

3 lines of code to schedule a background job using a simple script. Let's look at some of the really good example in the SCN that demonstrate how to use perl for basis tasks: Perl and SAP Adventures, Part 2

 

The perl script looks like this:

 

#!/usr/bin/perl -w

use strict;

use sapnwrfc;                                       # load the SAPNW::Rfc module

SAPNW::Rfc->load_config;                            # load connection parameters

my $rfc = SAPNW::Rfc->rfc_connect;                  # connect to SAP system

my $rcb = $rfc->function_lookup("TH_SERVER_LIST");  # look up the function module

my $tsl = $rcb->create_function_call;               # create the function call

$tsl->invoke;                                       # execute the function call

foreach my $row (@{$tsl->LIST}) {                   # loop through each row in LIST array

   print "Server is $row->{'NAME'}";                # print the server NAME value for each

}

$rfc->disconnect(); 

 

This is already easy but for someone that has never seen perl there are a lot of weird looking characters in the mix. Let's do this in Python:

 

#!/usr/bin/python
from pyrfc import Connection                 # load the pyrfc.Connection method
conn = Connection(user=<username>,           # logon to SAP
                  passwd=<password>,        

                  ashost=<hostname>,       
                  sysnr=<system number>,

                  client=<client>)

result=conn.call('TH_SERVER_LIST')           # execute the function module

for server in result['LIST']:                # loop over each row in the LIST list
    print "Server is " + server['NAME']      # print the NAME value for each server
conn.close()                                 # close the connection

 

Simple, elegant an very easy to read and understand.

 

What you need

 

You don't need a lot to get started:

 

  • Obviosuly Python. If it's not already part of your OS you can download it from Python.org. You need version 2.7.6 since SAP's pyrfc module is only avaiable for that version. Python 3.4 is not supported (yet?). There are also python bundles available that have a lot of modules already bundled in. There is also a portable Python version that does not require installation called WinPython.
  • The PyRFC module: PyRFC - The Python RFC Connector . The module has excellent installation instructions. just follow them step by step. You may be required to download additional python modules to get the PyRFC module going.
  • Depending on what you want to do you may need other modules. For example the logger module for easy logging, or  something to generate HTML coding. The amount available modules are endless in case the already vast standard functionality is not sufficient.
  • It will be easier to use a dedicated Python IDE (Integrated Development Environment). Syntax highlighting and context sensitive online help makes the development even easier. An overview of the different IDEs available can be found in this article: Comparison of Python IDEs for Development | Python Central. I personally use PyCharm but sometimes it can be annoyingly slow to start.

 

Another thing is training and documentation. There are many, many books and online courses available for free. For example Google's Python Class Or the free offerings from Codecademy. Or just work your way through the tons of example coding that is available all over the Internet.

 

Start with something useful: List of delta transports for a System Copy

We want to perform a system copy to refresh the QA system from the production system as part of a dress rehearsal.  After the system copy we need to import the transports that were in QA system but didn't make it to production yet. The way that is described here does not necessarily mean that it works for your environment. So don't accept this thing blindly without checking, actually better assume this script is not working at all and test it out in your environment.

 

Here are the steps we need to accomplish:

  • Logon to the source (PRD) and the target system (QAS)
  • download tables TPALOG from both systems
  • determine which transports are missing in the PRD system
  • determine the sequence of the missing transports
  • print the missing transports in the sequence they need to get applied.

 

Logging on

In this example we will store the logon information in a text file. This example the text contains a password stored in clear text. That's obviously not a good thing. In some later parts we will discuss ways around this but for now let's stick with this approach. This is the text file:

 

[target]
user = TESTUSER
passwd = A very secure password!
ashost = qasp00

client = 020
sysnr = 00
sid = QAS

 

[source]
user = TESTUSER
passwd = A very secure password, too!

ashost = prdpp00
client = 020
sysnr = 00
sid = PRD

 

The file syntax corresponds to a standard windows .ini file. The configuration section is stored in "[ ]" and its values below it. The file should reside in the same directory as the script with the name 'sapsystems.cfg'. It gets read by the Module ConfigParser which returns a dictionary object consisting of the values that belong to a configuration item.

 

The Script

 

 

from pyrfc import Connection
from ConfigParser import ConfigParser


config = ConfigParser()      
config.read('sapsystems.cfg')

source_transports = []
target_transports = []

params_source=config._sections['source'] #read the logon information of the source system
params_target=config._sections['target'] #read the logon information of the target system

target_conn = Connection(user=params_target['user'],
                         passwd=params_target['passwd'],
                         ashost=params_target['ashost'],
                         client=params_target['client'],
                         sysnr=params_target['sysnr'],
                         sid=params_target['sid'])

 

# that's a lot of typing. The connection information is returned in a data

# structure called 'dictionary'. Using the following syntax we basically 'unpack'

# the content of the structure and make this whole thing a lot simpler:

source_conn = Connection(**params_source)

 

# The information we need is stored in table TPALOG. since we don't
# want to download the entire table, we need to restrict the returned
# records. We are going to use funtion module RFC_READ_TABLE for this.
# the where clause there is stored in ABAP syntax. That means we need
# these statements:

target_where = "TARSYSTEM EQ '" + params_target['sid'] + \
               "' AND TRSTEP EQ 'I'"

source_where = "TARSYSTEM EQ '" + params_source['sid'] + \
               "' AND TRSTEP EQ 'I'"


# now let's read the data for the target system
target_result = target_conn.call('RFC_READ_TABLE',
                       QUERY_TABLE='TPALOG',
                       DELIMITER='|',
                       OPTIONS = [{'TEXT':target_where}])

 

# we are going to store the transports in a 'set'. A set can do
# lots of things that would otherwise very difficult to perform
# otherwise
list_transports_sequence=list()

 

# the following list will be used for the sequence later on
target_transports=set()

for row in target_result['DATA']:
    splitrow=row['WA'].split('|')
    # here we add the transport to the set
    target_transports.update(str(splitrow[1].strip()))
    # and here we append the import date and the transport as dictionary

    # to the list of all transports
    list_transports_sequence.append(dict(date=splitrow[0].strip(),
                                         transport=splitrow[1].strip()))

 

source_result = source_conn.call('RFC_READ_TABLE',
                       QUERY_TABLE='TPALOG',
                       DELIMITER='|',
                       OPTIONS = [{'TEXT':source_where}])

# the same for the transports in the source system
source_transports=set()

for row in source_result['DATA']:
    splitrow=row['WA'].split('|')
    source_transports.update(str(splitrow[1].strip()))

# now one of the cool things to do with sets:
missing_transports = target_transports - source_transports

# the returned set 'missing_transports' contains the list of

# transports that were imported into QAS but never made it to

# production. In the next step we need to determine the import sequence
for transport in missing_transports:
    # at first we delete all transports from the list of imported

    # transports that were imported into both systems already
    list_transports_sequence[:] = [dic for dic in list_transports_sequence \

                                   if dic.get('transport') != transport]

print("Missing transports in the sequence in which they need to get applied:")

# we need to order the list of the transports by import date.
for transport in sorted(list_transports_sequence,key= lambda tp: tp['date']):
    print(transport['transport'])

UI for VBScript

$
0
0

Hello community,

 

we discuss here the question of using UIs (User Interfaces) in the context of VBA. On the one hand it is possible to use the VBA forms or on the other hand it is possible to use the following ActiveX library: Quick Prompts. You find Quick Prompts here. With Quick Prompts it is easy possible to create an UI for VBS, e.g. to use it inside SAP GUI Scripting.

 

Here an example to create a form with the text input fields for the user name and the password. The passord is a masked input field.

 

'-Begin-----------------------------------------------------------------

 

  '-Directives----------------------------------------------------------

     Option Explicit


  '-Variables-----------------------------------------------------------

    Dim Form

 

  '-Main----------------------------------------------------------------

    Set Form = CreateObject("QuickPrompts.Form.2")

    If IsObject(Form) Then

 

      '-Setup form caption----------------------------------------------

        Form.Caption = "User Name/Password Sample"

 

      '-Create two textbox controls-------------------------------------

        Form.CreateControl "TextBox", "txtUserName", "&User Name:"

        Form.CreateControl "TextBox", "txtPassword", "&Password:"

 

      '-Set password flag on second text box ---------------------------

        Form.txtPassword.Password = vbTrue

 

      '-Display the dialog, quit if user cancels------------------------

        Do

        Loop Until Form.DoModal()

 

      '-Display result--------------------------------------------------

        MsgBox "User Name = " & form.txtUserName.value & _

          "  Password = " & form.txtPassword.value

 

    End If

 

'-End-------------------------------------------------------------------

 

qp.JPGqp2.JPG

Quick Prompts is from Topten Software and licensed under the Creative Commons Attribution-Noncommercial-Share Alike 2.5 Australia License.

 

Cheers

Stefan

Process Synchronization with VBScript

$
0
0

Hello community,

 

in a few posts we discuss the possibility to use multiple VBScript processes. With the Run command you can start different processes asynchronously. But after the start, you have not really a chance to synchronize the processes. Maybe via a file interface. For the process synchronization and communication offers Windows the IPC interface (Interprocess Communication).

 

There are different technics to synchronize and to communicate between processes, like

  • Events,
  • Semaphores,
  • FileMaps and
  • the ClipBoard
  • etc.

 

To use this possibilities I programmed a COM library which offers this technics. You find it here.

 

With COMIPC you can easily e.g. synchronize processes, look at the following example:

 

'-Begin-----------------------------------------------------------------


  '-Directives----------------------------------------------------------

    Option Explicit


  '-Constants-----------------------------------------------------------

    Const INFINITE = -1   


  '-Variables-----------------------------------------------------------

    Dim oIPC, hEvent, wsh, extVBS, rc


  '-Main----------------------------------------------------------------

    Set oIPC = CreateObject("COMIPC")

    If IsObject(oIPC) Then

   

      hEvent = oIPC.EventCreate("TestEvent", 0, 0)

     

      If hEvent Then


        extVBS = "Script 2.vbs"

        Set wsh = CreateObject("WScript.Shell")

        If IsObject(wsh) Then

          wsh.Run("wscript.exe " & extVBS)

          Set wsh = Nothing

        End If


        rc = oIPC.EventWait("TestEvent", INFINITE)

        rc = oIPC.EventClose(hEvent)

       

      End If 

      Set oIPC = Nothing

    End If

   

    MsgBox "End"     


'-End-------------------------------------------------------------------

 

We create an event, start a second script and wait until the event is set in the second script.

 

'-Begin-----------------------------------------------------------------


  '-Directives----------------------------------------------------------

    Option Explicit

   

  '-Variables-----------------------------------------------------------

    Dim oIPC, rc


  '-Main----------------------------------------------------------------

    Set oIPC = CreateObject("COMIPC")

    If IsObject(oIPC) Then

   

      MsgBox "Event"

   

      rc = oIPC.EventSet("TestEvent")

      Set oIPC = Nothing

    End If

   

'-End-------------------------------------------------------------------

 

If the event is set, in the second script, the first script will finish.

 

So it is e.g. possible to start different SAP GUI Scripts and to set different synchronization points. Also you can communicate between different processes via FileMap - File mapping is the association of a file's contents with a portion of the virtual address space of a process.

 

Enjoy it.

 

Cheers

Stefan


Quick and easy install of PyRFC on Windows

$
0
0

python-logo.png

Python is an easy to learn programming language. As the documentation on the official Python pagesays, "Python can be easy to pick up whether you're a first time programmer or you're experienced with other languages. Python is powerful... and fast; plays well with others; runs everywhere; is friendly & easy to learn; is Open".


The PyRFC module is the newest way to get externally running Python code integrated with SAP.  Srdjan Boskovic explains nicely why you would want a Python/ABAP stack. As he says, "Our language of choice for the web development is Python and a frequent question we hear is why we are using Python and not Java, the de-facto standard for the enterprise."


Setup and installation of PyRFC isn't too difficult on GNU/Linux and OS X. On Windows, it can be a little painful for Python noobies as the C-extension needs to be compiled, and by default Windows doesn't ship with a compiler. These instructions are geared more towards beginners and get you started in the barest possible time without having to compile, as I've pre-compiled the module for you. I've tested this process on Windows 7 and 8.


Quick setup process:


  • Install Python 2 32-bit version. Get Python from https://www.python.org/downloads/windows/ and choose to download for Windows. Then select "Latest Python 2 Release", which at the time of writing this was 2.7.8. I choose the "Windows x86 MSI Installer" file. When I install, I prefer to install "just for me", and I choose the option to add python.exe to Path

 

  • You'll need SAP's C-connector, NWRFC. And in order to get that you'll also need an "S-number" (SAP logon) that allows you to download software from the SAP support website. Here is a PDF telling you how to get the files you need from the SAP support site.


  • Extract the C-connector. Here's how: open a cmd shell (or PowerShell) and do: 


SAPCAR_315-20010451.EXE -xvf NWRFC_24-20004566

(Note that I used the Windows/IA32 versions of these files, and that the version numbers of the files that you download might not be the same, adjust accordingly!)


  • "Install" the C-connector. Put the "nwrfcsdk" folder that the above step created where you want it installed. For this exercise, I moved it to be under C:\Python27\


It will be necessary to adjust your environmental variables for the install location. Open environmental variables editor, which can be opened as follows: open Control Panel, go to System and Security, then  "System", "Advanced System Settings", then click the "Environmental Variables" button. Edit the "Path" environmental variable. This should already have C:\Python\27\ added to it, but we need to also add:

 

;C:\Python27\nwrfcsdk\lib

to the end of the list. Obviously put another path in if your path isn't C:\Python27\nwrfcsdk\lib


If you forget to set the Path, then your Python code won't be able to use the C-connector and you'll get an error message: "ImportError: DLL load failed"


  • We need setuptools so that we have easy_install. Download distribute_setup.py. From a cmd prompt install it like so:


python distribute_setup.py


  • And now for our Trademarked "Secret Sauce" which is why you're reading this article! Use easy_install to install this prebuilt binary Python .egg file for PyRFC (md5 846946fe9c8596d186dbadb9258eca66).

 

From a cmd prompt install as follows:

 

C:\Python27\Scripts\easy_install-2.7.exe pyrfc-1.9.4-py2.7-win32.egg
  • Finally, we can test it:


Create a config file, sapnwrfc.cfg and insert content like this, obviously set the correct ashost (sap system), client, sysnr, user, passwd:


[connection]

# sap system ip

ashost = xxxxxxx

# sap client id

client = 001

# sap router string, optional, format like /H/x.x.x.x/...

saprouter =

# sap system number

sysnr = 00

# sap username

user = DEVELOPER

# sap password

passwd = xxxxxxx2

 

[gateway]

# gateway host name

gwhost =

# gateway server name

ghserv =

# name under which the python connector will register itself

program_id = PYRFC_SERVER1

# sap router string, optional

saprouter =

 


Create a test program file "get_system_info.py" and paste the following into it:

 

#!/usr/bin/env python
from pyrfc import Connection, ABAPApplicationError, ABAPRuntimeError, LogonError, CommunicationError
from ConfigParser import ConfigParser
from pprint import PrettyPrinter

def main():
        try:        config = ConfigParser()        config.read('sapnwrfc.cfg')        params_connection = config._sections['connection']        conn = Connection(**params_connection)        result = conn.call('RFC_SYSTEM_INFO')        pp = PrettyPrinter(indent=4)        pp.pprint(result)    except CommunicationError:        print u"Could not connect to server."        raise    except LogonError:        print u"Could not log in. Wrong credentials?"        raise    except (ABAPApplicationError, ABAPRuntimeError):        print u"An error occurred."        raise

if __name__ == '__main__':
    main()


Now, if you run:


python get_system_info.py


You should get a response with system information, like this:


{   u'CURRENT_RESOURCES': 7,    u'MAXIMAL_RESOURCES': 9,    u'RECOMMENDED_DELAY': 0,    u'RFCSI_EXPORT': {   u'RFCCHARTYP': u'4103',                         u'RFCDATABS': u'NPL',                         u'RFCDAYST': u'',                         u'RFCDBHOST': u'WDFLBMD6865',                         u'RFCDBSYS': u'ADABAS D',                         u'RFCDEST': u'wdflbmd6865_NPL_00',                         u'RFCFLOTYP': u'IE3',                         u'RFCHOST': u'wdflbmd6',                         u'RFCHOST2': u'wdflbmd6865',                         u'RFCINTTYP': u'LIT',                         u'RFCIPADDR': u'172.31.xx.xxx',                         u'RFCIPV6ADDR': u'172.31.xx.xxx',                         u'RFCKERNRL': u'740',                         u'RFCMACH': u'  390',                         u'RFCOPSYS': u'Linux',                         u'RFCPROTO': u'011',                         u'RFCSAPRL': u'740',                         u'RFCSI_RESV': u'',                         u'RFCSYSID': u'NPL',                         u'RFCTZONE': u'     0'}}


It works! And how easy was that?


Notes:

  1. I've made some assumptions in this document. One assumption is that you're wanting the 32-bit version of the software (this will still work on 64-bit systems!)
  2. You really should investigate using pip for installing further Python packages. For installing pip on windows see this link
  3. ...and you owe it to yourself to install virtualenv which allows you to install Python modules independently from the global Python install! See the same link as above.
  4. Here is a binary egg file for python-sapnwrfc (md5 358ca360a290e52c5a55d90fb3998abb) (This file is Piers Harding's wrapper module which was an earlier module that we used before PyRFC came along, in case you'd rather use his module instead of PyRFC).

How to use actual SAP NetWeaver RFC Library with Python - The Connection

$
0
0

Hello community,

 

I am not a Python programmer, but this week I had a discussion with a Python programmer about the using of actual SAP NetWeaver RFC library with Python. He told me, that he uses the library pysaprfc, which bases on the classic librfc32.dll. So I am a little bit surprised about his decision, because on the one hand Python offers via ctypes a complete interface to use the actual SAP NetWeaver RFC library. On the other hand is the classic RFC library (a little bit) outdated, e.g. it doesn't supports all data types - look here in the ActiveX context. I am searching in the internet and I am surprised again, I can't find an example snippet or something else. This was my launch to create the following examples.

 

The first example shows how to connect an SAP system:

 

#-Begin-----------------------------------------------------------------

 

#-Packages--------------------------------------------------------------

from ctypes import *

 

#-Structures------------------------------------------------------------

class RFC_ERROR_INFO(Structure):

  _fields_ = [("code", c_long),

              ("group", c_long),

              ("key", c_wchar * 128),

              ("message", c_wchar * 512),

              ("abapMsgClass", c_wchar * 21),

              ("abapMsgType", c_wchar * 2),

              ("abapMsgNumber", c_wchar * 4),

              ("abapMsgV1", c_wchar * 51),

              ("abapMsgV2", c_wchar * 51),

              ("abapMsgV3", c_wchar * 51),

              ("abapMsgV4", c_wchar * 51)]

 

class RFC_CONNECTION_PARAMETER(Structure):

  _fields_ = [("name", c_wchar_p),

              ("value", c_wchar_p)]

 

#-Main------------------------------------------------------------------

ErrInf = RFC_ERROR_INFO; RfcErrInf = ErrInf()

ConnParams = RFC_CONNECTION_PARAMETER * 5; RfcConnParams = ConnParams()

 

SAPNWRFC = "sapnwrfc.dll"

SAP = windll.LoadLibrary(SAPNWRFC)

 

#-Prototypes------------------------------------------------------------

SAP.RfcOpenConnection.argtypes = [POINTER(ConnParams), c_ulong, \

  POINTER(ErrInf)]

SAP.RfcOpenConnection.restype = c_void_p

SAP.RfcCloseConnection.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcCloseConnection.restype = c_ulong

 

#-Connection parameters-------------------------------------------------

RfcConnParams[0].name = "ASHOST"; RfcConnParams[0].value = "ABAP"

RfcConnParams[1].name = "SYSNR" ; RfcConnParams[1].value = "00"

RfcConnParams[2].name = "CLIENT"; RfcConnParams[2].value = "001"

RfcConnParams[3].name = "USER"  ; RfcConnParams[3].value = "BCUSER"

RfcConnParams[4].name = "PASSWD"; RfcConnParams[4].value = "minisap"

 

 

hRFC = SAP.RfcOpenConnection(RfcConnParams, 5, RfcErrInf)

if hRFC != None:

 

  windll.user32.MessageBoxW(None, "Check connection with TAC SMGW", \

    "", 0)

 

  #---------------------------------------------------------------------

  #-

  #- Check connection with TAC SMGW in the SAP system

  #-

  #---------------------------------------------------------------------

 

  rc = SAP.RfcCloseConnection(hRFC, RfcErrInf)

 

else:

  print(RfcErrInf.key)

  print(RfcErrInf.message)

 

#-End-------------------------------------------------------------------

 

0101_Connect.jpg

 

The second example shows how to get the version number of the library:

 

# -*- coding: iso-8859-15 -*-

#-Begin-----------------------------------------------------------------

 

#-Include---------------------------------------------------------------

FileName = "sapnwrfc.py"

exec(compile(open(FileName).read(), FileName, "exec"))

 

#-Import----------------------------------------------------------------

import sys

import platform

 

#-Main------------------------------------------------------------------

MajorVersion = c_ulong(0)

MinorVersion = c_ulong(0)

PatchLevel = c_ulong(0)

Version = SAP.RfcGetVersion(MajorVersion, MinorVersion, PatchLevel)

 

print("MajorVersion: " + str(MajorVersion.value))

print("MinorVersion: " + str(MinorVersion.value))

print("PatchLevel: " + str(PatchLevel.value))

print("Version: " + Version)

 

del SAP

 

PyVer = sys.version_info

print("\nPython version: " + str(PyVer.major) + "." + \

  str(PyVer.minor) + "." + str(PyVer.micro) + " " + \

  str(platform.architecture()[0]))

 

print("Operating system: " + str(platform.system()) + " " + \

  str(platform.release()) + " " + str(platform.machine()))

 

#-End-------------------------------------------------------------------

 

As you can see I am a newbie in Python, but I think this snippets are a good precondition to intensify the using of actual SAP NetWeaver RFC library with Python directly. I developed this examples with the actual Python version 3.4.1, in an Windows environment with the actual SAP NetWeaver RFC library 720 PL24.

 

Cheers

Stefan

How to use actual SAP NetWeaver RFC Library with Python - Invoke Functions

$
0
0

Hello community,

 

yesterday I presented here the first steps how to connect an SAP system via Python 3 without any additional packages. Here now the next steps, how to invoke functions. You will see how easy it is now, after the first step of connection - it is not more than a prolongation.

 

We start with an ordinary ping in two variants. The first variant explains how to invoke a RFC function, in our case RFC_PING. The second variant shows how to use the RfcPing function of the RFC library. In the focus of this example is the invocation of the RFC function call.

To invoke a RFC function you must get at first the description of the function via RfcGetFunctionDesc. The next step is to create the function with RfcCreateFunction and now you can invoke it. Last but not least you must destroy the created function via RfcDestroyFunction. Look at the example below.

 

#-Begin-----------------------------------------------------------------

 

#-Packages--------------------------------------------------------------

from ctypes import *

 

#-Structures------------------------------------------------------------

class RFC_ERROR_INFO(Structure):

  _fields_ = [("code", c_long),

              ("group", c_long),

              ("key", c_wchar * 128),

              ("message", c_wchar * 512),

              ("abapMsgClass", c_wchar * 21),

              ("abapMsgType", c_wchar * 2),

              ("abapMsgNumber", c_wchar * 4),

              ("abapMsgV1", c_wchar * 51),

              ("abapMsgV2", c_wchar * 51),

              ("abapMsgV3", c_wchar * 51),

              ("abapMsgV4", c_wchar * 51)]

 

class RFC_CONNECTION_PARAMETER(Structure):

  _fields_ = [("name", c_wchar_p),

              ("value", c_wchar_p)]

 

#-Constants-------------------------------------------------------------

RFC_OK = 0

 

#-Main------------------------------------------------------------------

ErrInf = RFC_ERROR_INFO; RfcErrInf = ErrInf()

ConnParams = RFC_CONNECTION_PARAMETER * 5; RfcConnParams = ConnParams()

 

SAPNWRFC = "sapnwrfc.dll"

SAP = windll.LoadLibrary(SAPNWRFC)

 

#-Prototypes------------------------------------------------------------

SAP.RfcOpenConnection.argtypes = [POINTER(ConnParams), c_ulong, \

  POINTER(ErrInf)]

SAP.RfcOpenConnection.restype = c_void_p

 

SAP.RfcCloseConnection.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcCloseConnection.restype = c_ulong

 

SAP.RfcGetFunctionDesc.argtypes = [c_void_p, c_wchar_p, POINTER(ErrInf)]

SAP.RfcGetFunctionDesc.restype = c_void_p

 

SAP.RfcCreateFunction.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcCreateFunction.restype = c_void_p

 

SAP.RfcInvoke.argtypes = [c_void_p, c_void_p, POINTER(ErrInf)]

SAP.RfcInvoke.restype = c_ulong

 

SAP.RfcDestroyFunction.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcDestroyFunction.restype = c_ulong

 

SAP.RfcPing.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcPing.restype = c_ulong

 

#-Connection parameters-------------------------------------------------

RfcConnParams[0].name = "ASHOST"; RfcConnParams[0].value = "ABAP"

RfcConnParams[1].name = "SYSNR" ; RfcConnParams[1].value = "00"

RfcConnParams[2].name = "CLIENT"; RfcConnParams[2].value = "001"

RfcConnParams[3].name = "USER"  ; RfcConnParams[3].value = "BCUSER"

RfcConnParams[4].name = "PASSWD"; RfcConnParams[4].value = "minisap"

 

 

hRFC = SAP.RfcOpenConnection(RfcConnParams, 5, RfcErrInf)

if hRFC != None:

 

  #-Variant 1-----------------------------------------------------------

  hFuncDesc = SAP.RfcGetFunctionDesc(hRFC, "RFC_PING", RfcErrInf)

  if hFuncDesc != 0:

    hFunc = SAP.RfcCreateFunction(hFuncDesc, RfcErrInf)

    if hFunc != 0:

      if SAP.RfcInvoke(hRFC, hFunc, RfcErrInf) == RFC_OK:

        print("Ping successful")

      else:

        print("Ping not successful")

      rc = SAP.RfcDestroyFunction(hFunc, RfcErrInf)

 

  #-Variant 2-----------------------------------------------------------

  if SAP.RfcPing(hRFC, RfcErrInf) == RFC_OK:

    print("Ping successful")

  else:

    print("Ping not successful")

 

  rc = SAP.RfcCloseConnection(hRFC, RfcErrInf)

 

else:

  print(RfcErrInf.key)

  print(RfcErrInf.message)

 

del SAP

 

#-End-------------------------------------------------------------------

 

In the next example I demonstrate how to invoke the function RFC_SYSTEM_INFO. It is the same way as I described above, until the invocation of the function. After the invocation we get the information of the function module via RfcGetStructure and RfcGetChars, in our case SYSID, HOST, DBHOST and DBSYS - that's all. Look at the example below:

 

#-Begin-----------------------------------------------------------------


#-Packages--------------------------------------------------------------

from ctypes import *


#-Structures------------------------------------------------------------

class RFC_ERROR_INFO(Structure):

  _fields_ = [("code", c_long),

              ("group", c_long),

              ("key", c_wchar * 128),

              ("message", c_wchar * 512),

              ("abapMsgClass", c_wchar * 21),

              ("abapMsgType", c_wchar * 2),

              ("abapMsgNumber", c_wchar * 4),

              ("abapMsgV1", c_wchar * 51),

              ("abapMsgV2", c_wchar * 51),

              ("abapMsgV3", c_wchar * 51),

              ("abapMsgV4", c_wchar * 51)]

 

class RFC_CONNECTION_PARAMETER(Structure):

  _fields_ = [("name", c_wchar_p),

              ("value", c_wchar_p)]

 

#-Constants-------------------------------------------------------------

RFC_OK = 0

 

#-Main------------------------------------------------------------------

ErrInf = RFC_ERROR_INFO; RfcErrInf = ErrInf()

ConnParams = RFC_CONNECTION_PARAMETER * 5; RfcConnParams = ConnParams()

 

SAPNWRFC = "sapnwrfc.dll"

SAP = windll.LoadLibrary(SAPNWRFC)

 

#-Prototypes------------------------------------------------------------

SAP.RfcOpenConnection.argtypes = [POINTER(ConnParams), c_ulong, \

  POINTER(ErrInf)]

SAP.RfcOpenConnection.restype = c_void_p

 

SAP.RfcCloseConnection.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcCloseConnection.restype = c_ulong

 

SAP.RfcGetFunctionDesc.argtypes = [c_void_p, c_wchar_p, POINTER(ErrInf)]

SAP.RfcGetFunctionDesc.restype = c_void_p

 

SAP.RfcCreateFunction.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcCreateFunction.restype = c_void_p

 

SAP.RfcInvoke.argtypes = [c_void_p, c_void_p, POINTER(ErrInf)]

SAP.RfcInvoke.restype = c_ulong

 

SAP.RfcDestroyFunction.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcDestroyFunction.restype = c_ulong

 

SAP.RfcGetStructure.argtypes = [c_void_p, c_wchar_p, \

  POINTER(c_void_p), POINTER(ErrInf)]

SAP.RfcGetStructure.restype = c_ulong

 

SAP.RfcGetChars.argtypes = [c_void_p, c_wchar_p, c_void_p, c_ulong, \

  POINTER(ErrInf)]

SAP.RfcGetChars.restype = c_ulong

 

#-Connection parameters-------------------------------------------------

RfcConnParams[0].name = "ASHOST"; RfcConnParams[0].value = "ABAP"

RfcConnParams[1].name = "SYSNR" ; RfcConnParams[1].value = "00"

RfcConnParams[2].name = "CLIENT"; RfcConnParams[2].value = "001"

RfcConnParams[3].name = "USER"  ; RfcConnParams[3].value = "BCUSER"

RfcConnParams[4].name = "PASSWD"; RfcConnParams[4].value = "minisap"

 

 

hRFC = SAP.RfcOpenConnection(RfcConnParams, 5, RfcErrInf)

if hRFC != None:


  hFuncDesc = SAP.RfcGetFunctionDesc(hRFC, "RFC_SYSTEM_INFO", RfcErrInf)

  if hFuncDesc != 0:

    hFunc = SAP.RfcCreateFunction(hFuncDesc, RfcErrInf)

    if hFunc != 0:

      if SAP.RfcInvoke(hRFC, hFunc, RfcErrInf) == RFC_OK:

        hStruct = c_void_p(0)

        if SAP.RfcGetStructure(hFunc, "RFCSI_EXPORT", hStruct, \

          RfcErrInf) == RFC_OK:

 

          SAPHost = create_unicode_buffer(8 + 1)

          rc = SAP.RfcGetChars(hStruct, "RFCHOST", SAPHost, 8, \

            RfcErrInf)

          print(SAPHost.value)

 

          SAPSysID = create_unicode_buffer(8 + 1)

          rc = SAP.RfcGetChars(hStruct, "RFCSYSID", SAPSysID, 8, \

            RfcErrInf)

          print(SAPSysID.value)

 

          SAPDBHost = create_unicode_buffer(32 + 1)

          rc = SAP.RfcGetChars(hStruct, "RFCDBHOST", SAPDBHost, 32, \

            RfcErrInf)

          print(SAPDBHost.value)

 

          SAPDBSys = create_unicode_buffer(10 + 1)

          rc = SAP.RfcGetChars(hStruct, "RFCDBSYS", SAPDBSys, 10, \

            RfcErrInf)

          print(SAPDBSys.value)

 

      rc = SAP.RfcDestroyFunction(hFunc, RfcErrInf)

 

  rc = SAP.RfcCloseConnection(hRFC, RfcErrInf)

 

else:

  print(RfcErrInf.key)

  print(RfcErrInf.message)

 

del SAP

 

#-End-------------------------------------------------------------------

 

With the COM Connector (CCo) I published a lot of examples, which can be almost identical implemented in Python. All you have to do is to define the argument and result type of the prototype of the function. As you can see it is easy to use SAP NetWeaver RFC library with Python 3. I hope this examples opens the gate wide for the future development of this kind of requirements.

 

Cheers

Stefan

How to use actual SAP NetWeaver RFC Library with Python - Execute ABAP Report

$
0
0

Hello community,

 

I presented until now in the series "How to use actual SAPNetWeaver RFC Library with Pyhton" :

 

Here now an example how to define and run ABAP code from Python.

All constants, structures and prototypes are in the quasi-include file sapnwrfc.py - you find it below.

The ABAP report itself is an array of strings. We use the function module (FM) RFC_ABAP_INSTALL_AND_RUN to implement and execute the ABAP program. We set the report line by line in the PROGRAM table of the FM, invoke the function and get a result back in the WRITES table. We read each line of ZEILE and add it to a Result string.

 

# -*- coding: iso-8859-15 -*-
#-Begin-----------------------------------------------------------------

 

 

#-Include---------------------------------------------------------------
FileName = "sapnwrfc.py"
exec(compile(open(FileName).read(), FileName, "exec"))

 

#-Main------------------------------------------------------------------

 

#-Connection parameters-------------------------------------------------
RfcConnParams[0].name = "ASHOST"; RfcConnParams[0].value = "NSP"
RfcConnParams[1].name = "SYSNR" ; RfcConnParams[1].value = "00"
RfcConnParams[2].name = "CLIENT"; RfcConnParams[2].value = "001"
RfcConnParams[3].name = "USER"  ; RfcConnParams[3].value = "BCUSER"
RfcConnParams[4].name = "PASSWD"; RfcConnParams[4].value = "minisap"

 

#-ABAPReport------------------------------------------------------------
#-
#- Code your ABAP report here. The length of each line must be equal or
#- less than 72 characters.
#-
#-----------------------------------------------------------------------
ABAP=[]
ABAP.append("Report zTest Line-Size 256.")
ABAP.append("Write: 'Hello World from'.")
ABAP.append("Write: sy-sysid.")

 


hRFC = SAP.RfcOpenConnection(RfcConnParams, 5, RfcErrInf)
if hRFC != None:

 

  charBuffer = create_unicode_buffer(256 + 1)
  Result = ""

 

  hFuncDesc = SAP.RfcGetFunctionDesc(hRFC, "RFC_ABAP_INSTALL_AND_RUN", \
    RfcErrInf)

  if hFuncDesc != 0:
    hFunc = SAP.RfcCreateFunction(hFuncDesc, RfcErrInf)
    if hFunc != 0:

      hTable = c_void_p(0)

 

      #-Writes the report into the PROGRAM table------------------------
      if SAP.RfcGetTable(hFunc, "PROGRAM", hTable, RfcErrInf) == RFC_OK:
        for i in range(0, len(ABAP)):
          hRow = SAP.RfcAppendNewRow(hTable, RfcErrInf)
          rc = SAP.RfcSetChars(hRow, "LINE", ABAP[i], len(ABAP[i]), \
            RfcErrInf)

 

        if SAP.RfcInvoke(hRFC, hFunc, RfcErrInf) == RFC_OK:
          #-Gets the result from the WRITES table-----------------------
          if SAP.RfcGetTable(hFunc, "WRITES", hTable, RfcErrInf) == RFC_OK:
            RowCount = c_ulong(0)
            rc = SAP.RfcGetRowCount(hTable, RowCount, RfcErrInf)
            rc = SAP.RfcMoveToFirstRow(hTable, RfcErrInf)
            for i in range(0, RowCount.value):
              hRow = SAP.RfcGetCurrentRow(hTable, RfcErrInf)
              rc = SAP.RfcGetChars(hRow, "ZEILE", charBuffer, 256, \
                RfcErrInf)
              Result = Result + charBuffer.value
              if i < RowCount.value:
                rc = SAP.RfcMoveToNextRow(hTable, RfcErrInf)

 

            #-Shows the result------------------------------------------
            print(Result)

 

      rc = SAP.RfcDestroyFunction(hFunc, RfcErrInf)

 

  rc = SAP.RfcCloseConnection(hRFC, RfcErrInf)

 

else:
  print(RfcErrInf.key)
  print(RfcErrInf.message)

 

del SAP

 

#-End-------------------------------------------------------------------

 

As you can see, it is no problem to define and execute an ABAP program inside Python.

 

Enjoy it.

 

Cheers

Stefan

 

 

P.S. Here the file sapnwrfc.py - at the end of the post.

How to use actual SAP NetWeaver RFC Library with Python - Read Table

$
0
0

Hello community,

 

I presented until now in the series "How to use actual SAPNetWeaver RFC Library with Pyhton" :

 

Here now an example how to read a table with the function module (FM) RFC_READ_TABLE. You need the file sapnwrfc.py from here - look at the end of the posting.

After the connection we get the function description of the FM, in our case RFC_READ_TABLE. We set the arguments QUERY_TABLE, in our case USR01, and the DELIMITER. We invoke the FM and print the result line by line. The result is in the DATA table, in the field WA.

 

# -*- coding: iso-8859-15 -*-

#-Begin-----------------------------------------------------------------

 

#-Include---------------------------------------------------------------

FileName = "sapnwrfc.py"

exec(compile(open(FileName).read(), FileName, "exec"))

 

#-Main------------------------------------------------------------------

 

#-Connection parameters-------------------------------------------------

RfcConnParams[0].name = "ASHOST"; RfcConnParams[0].value = "ABAP"

RfcConnParams[1].name = "SYSNR" ; RfcConnParams[1].value = "00"

RfcConnParams[2].name = "CLIENT"; RfcConnParams[2].value = "001"

RfcConnParams[3].name = "USER"  ; RfcConnParams[3].value = "BCUSER"

RfcConnParams[4].name = "PASSWD"; RfcConnParams[4].value = "minisap"

 

TableName = "USR01"

 

hRFC = SAP.RfcOpenConnection(RfcConnParams, 5, RfcErrInf)

if hRFC != None:

 

  charBuffer = create_unicode_buffer(512 + 1)

 

  hFuncDesc = SAP.RfcGetFunctionDesc(hRFC, "RFC_READ_TABLE", RfcErrInf)

  if hFuncDesc != 0:

    hFunc = SAP.RfcCreateFunction(hFuncDesc, RfcErrInf)

    if hFunc != 0:

 

      rc = SAP.RfcSetChars(hFunc, "QUERY_TABLE", TableName, \

        len(TableName), RfcErrInf)

      rc = SAP.RfcSetChars(hFunc, "DELIMITER", "~", 1, RfcErrInf)

 

      if SAP.RfcInvoke(hRFC, hFunc, RfcErrInf) == RFC_OK:

 

        hTable = c_void_p(0)

        if SAP.RfcGetTable(hFunc, "DATA", hTable, RfcErrInf) == RFC_OK:

 

          RowCount = c_ulong(0)

          rc = SAP.RfcGetRowCount(hTable, RowCount, RfcErrInf)

          rc = SAP.RfcMoveToFirstRow(hTable, RfcErrInf)

          for i in range(0, RowCount.value):

            hRow = SAP.RfcGetCurrentRow(hTable, RfcErrInf)

            rc = SAP.RfcGetChars(hRow, "WA", charBuffer, 512, RfcErrInf)

            print(charBuffer.value)

            if i < RowCount.value:

              rc = SAP.RfcMoveToNextRow(hTable, RfcErrInf)

 

      rc = SAP.RfcDestroyFunction(hFunc, RfcErrInf)

 

  rc = SAP.RfcCloseConnection(hRFC, RfcErrInf)

 

else:

  print(RfcErrInf.key)

  print(RfcErrInf.message)

 

del SAP

 

#-End-------------------------------------------------------------------

 

Cheers

Stefan

How to use actual SAP NetWeaver RFC Library with Python - Python Server Application

$
0
0

Hello community,

 

I presented until now in the series "How to use actual SAPNetWeaver RFC Library with Pyhton" :

 

Here now an example how to use Python as server application from ABAP.

 

We start with the transaction code SM59 to configure the RFC destination of the Python server application.

001.JPG

Here the Python source:

 

# -*- coding: iso-8859-15 -*-

#-Begin-----------------------------------------------------------------

 

#-Packages--------------------------------------------------------------

from threading import Thread

import time

 

#-Include---------------------------------------------------------------

FileName = "sapnwrfc.py"

exec(compile(open(FileName).read(), FileName, "exec"))

 

#-Asynchronous function-------------------------------------------------

def SetQuit():

  global Quit

  windll.user32.MessageBoxW(None, "Stop Python server", "", 0)

  Quit = True

 

#-ABAP CallBack---------------------------------------------------------

def ABAPCall(rfcHandle, funcHandle, RfcErrInf):

  windll.user32.MessageBoxW(None, "ABAP calling", "", 0)

  return RFC_OK

 

#-Main------------------------------------------------------------------

Quit = False

t = Thread(target=SetQuit); t.start()

 

#-Connection parameters-------------------------------------------------

RfcSConParams[0].name = "PROGRAM_ID"; RfcSConParams[0].value = "PYTHONSERVER"

RfcSConParams[1].name = "GWHOST"    ; RfcSConParams[1].value = "ABAP"

RfcSConParams[2].name = "GWSERV"    ; RfcSConParams[2].value = "sapgw00"

 

ABAPFunc = WINFUNCTYPE(c_ulong, c_void_p, c_void_p, POINTER(ErrInf))

hDesc = SAP.RfcCreateFunctionDesc("ABAPCall", RfcErrInf)

if hDesc != None:

  if SAP.RfcInstallServerFunction("NSP", hDesc, ABAPFunc(ABAPCall), \

    RfcErrInf) == RFC_OK:

 

    hCon = SAP.RfcRegisterServer(RfcSConParams, 3, RfcErrInf)

    if hCon != None:

      rc = RFC_OK

      while (rc == RFC_OK, rc == RFC_RETRY):

        rc = SAP.RfcListenAndDispatch(hCon, 1, RfcErrInf)

        time.sleep(0.250)

        if Quit == True:

          break

    else:

      print(RfcErrInf.key); print(RfcErrInf.message)

 

  else:

    print(RfcErrInf.key); print(RfcErrInf.message)

 

  rc = SAP.RfcDestroyFunctionDesc(hDesc, RfcErrInf)

 

else:

  print(RfcErrInf.key); print(RfcErrInf.message)

 

del SAP

 

#-End-------------------------------------------------------------------

 

We define at first an asynchonous thread, to set a global variable to break the execution of the server, when we no longer need it. Then we define the callback function, which can be called from ABAP - in our case only a simple message box. We create an empty function description with RfcCreateFunctionDesc and install, with this description, the callback function on the SAP system with RfcInstallServerFunction. Now we register the server connection at an SAP gateway via RFCRegisterServer and listen for incoming RFC calls with RfcListenAndDispatch. Very simple, isn't it?

 

Now we can use the Python function ABAPCall from ABAP like this:

 

"-Begin-----------------------------------------------------------------

  Program ZTEST.


    Call Function 'ABAPCall' Destination 'PYTHONSERVER'.

 

"-End-------------------------------------------------------------------

 

And this is the result:

002.JPG

As you can see, it is very easy to use Python as SAP server application.

 

Cheers

Stefan

 

P.S. Here  the updated include file sapnwrfc.py:

 

#-Begin-----------------------------------------------------------------

 

#-Packages--------------------------------------------------------------

from ctypes import *

import platform, os

 

#-Structures------------------------------------------------------------

class RFC_ERROR_INFO(Structure):

    _fields_ = [("code", c_long),

                ("group", c_long),

                ("key", c_wchar * 128),

                ("message", c_wchar * 512),

                ("abapMsgClass", c_wchar * 21),

                ("abapMsgType", c_wchar * 2),

                ("abapMsgNumber", c_wchar * 4),

                ("abapMsgV1", c_wchar * 51),

                ("abapMsgV2", c_wchar * 51),

                ("abapMsgV3", c_wchar * 51),

                ("abapMsgV4", c_wchar * 51)]

 

class RFC_CONNECTION_PARAMETER(Structure):

    _fields_ = [("name", c_wchar_p),

                ("value", c_wchar_p)]

 

 

#-Constants-------------------------------------------------------------

 

#-RFC_RC - RFC return codes---------------------------------------------

RFC_OK = 0

RFC_COMMUNICATION_FAILURE = 1

RFC_LOGON_FAILURE = 2

RFC_ABAP_RUNTIME_FAILURE = 3

RFC_ABAP_MESSAGE = 4

RFC_ABAP_EXCEPTION = 5

RFC_CLOSED = 6

RFC_CANCELED = 7

RFC_TIMEOUT = 8

RFC_MEMORY_INSUFFICIENT = 9

RFC_VERSION_MISMATCH = 10

RFC_INVALID_PROTOCOL = 11

RFC_SERIALIZATION_FAILURE = 12

RFC_INVALID_HANDLE = 13

RFC_RETRY = 14

RFC_EXTERNAL_FAILURE = 15

RFC_EXECUTED = 16

RFC_NOT_FOUND = 17

RFC_NOT_SUPPORTED = 18

RFC_ILLEGAL_STATE = 19

RFC_INVALID_PARAMETER = 20

RFC_CODEPAGE_CONVERSION_FAILURE = 21

RFC_CONVERSION_FAILURE = 22

RFC_BUFFER_TOO_SMALL = 23

RFC_TABLE_MOVE_BOF = 24

RFC_TABLE_MOVE_EOF = 25

RFC_START_SAPGUI_FAILURE = 26

RFC_ABAP_CLASS_EXCEPTION = 27

RFC_UNKNOWN_ERROR = 28

RFC_AUTHORIZATION_FAILURE = 29

 

#-RFCTYPE - RFC data types----------------------------------------------

RFCTYPE_CHAR = 0

RFCTYPE_DATE = 1

RFCTYPE_BCD = 2

RFCTYPE_TIME = 3

RFCTYPE_BYTE = 4

RFCTYPE_TABLE = 5

RFCTYPE_NUM = 6

RFCTYPE_FLOAT = 7

RFCTYPE_INT = 8

RFCTYPE_INT2 = 9

RFCTYPE_INT1 = 10

RFCTYPE_NULL = 14

RFCTYPE_ABAPOBJECT = 16

RFCTYPE_STRUCTURE = 17

RFCTYPE_DECF16 = 23

RFCTYPE_DECF34 = 24

RFCTYPE_XMLDATA = 28

RFCTYPE_STRING = 29

RFCTYPE_XSTRING = 30

RFCTYPE_BOX = 31

RFCTYPE_GENERIC_BOX = 32

 

#-RFC_UNIT_STATE - Processing status of a background unit---------------

RFC_UNIT_NOT_FOUND = 0

RFC_UNIT_IN_PROCESS = 1

RFC_UNIT_COMMITTED = 2

RFC_UNIT_ROLLED_BACK = 3

RFC_UNIT_CONFIRMED = 4

 

#-RFC_CALL_TYPE - Type of an incoming function call---------------------

RFC_SYNCHRONOUS = 0

RFC_TRANSACTIONAL = 1

RFC_QUEUED = 2

RFC_BACKGROUND_UNIT = 3

 

#-RFC_DIRECTION - Direction of a function module parameter--------------

RFC_IMPORT = 1

RFC_EXPORT = 2

RFC_CHANGING = RFC_IMPORT + RFC_EXPORT

RFC_TABLES = 4 + RFC_CHANGING

 

#-RFC_CLASS_ATTRIBUTE_TYPE - Type of an ABAP object attribute-----------

RFC_CLASS_ATTRIBUTE_INSTANCE = 0

RFC_CLASS_ATTRIBUTE_CLASS = 1

RFC_CLASS_ATTRIBUTE_CONSTANT = 2

 

#-RFC_METADATA_OBJ_TYPE - Ingroup repository----------------------------

RFC_METADATA_FUNCTION = 0

RFC_METADATA_TYPE = 1

RFC_METADATA_CLASS = 2

 

 

#-Variables-------------------------------------------------------------

ErrInf = RFC_ERROR_INFO; RfcErrInf = ErrInf()

ConnParams = RFC_CONNECTION_PARAMETER * 5; RfcConnParams = ConnParams()

SConParams = RFC_CONNECTION_PARAMETER * 3; RfcSConParams = SConParams()

 

 

#-Library---------------------------------------------------------------

if str(platform.architecture()[0]) == "32bit":

  os.environ['PATH'] += ";C:\\SAPRFCSDK\\32bit"

  SAPNWRFC = "C:\\SAPRFCSDK\\32bit\\sapnwrfc.dll"

elif str(platform.architecture()[0]) == "64bit":

  os.environ['PATH'] += ";C:\\SAPRFCSDK\\64bit"

  SAPNWRFC = "C:\\SAPRFCSDK\\64bit\\sapnwrfc.dll"

SAP = windll.LoadLibrary(SAPNWRFC)

 

 

#-Prototypes------------------------------------------------------------

SAP.RfcAppendNewRow.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcAppendNewRow.restype = c_void_p

 

SAP.RfcCloseConnection.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcCloseConnection.restype = c_ulong

 

SAP.RfcCreateFunction.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcCreateFunction.restype = c_void_p

 

SAP.RfcCreateFunctionDesc.argtypes = [c_wchar_p, POINTER(ErrInf)]

SAP.RfcCreateFunctionDesc.restype = c_void_p

 

SAP.RfcDestroyFunction.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcDestroyFunction.restype = c_ulong

 

SAP.RfcDestroyFunctionDesc.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcDestroyFunctionDesc.restype = c_ulong

 

SAP.RfcGetChars.argtypes = [c_void_p, c_wchar_p, c_void_p, c_ulong, \

  POINTER(ErrInf)]

SAP.RfcGetChars.restype = c_ulong

 

SAP.RfcGetCurrentRow.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcGetCurrentRow.restype = c_void_p

 

SAP.RfcGetFunctionDesc.argtypes = [c_void_p, c_wchar_p, POINTER(ErrInf)]

SAP.RfcGetFunctionDesc.restype = c_void_p

 

SAP.RfcGetRowCount.argtypes = [c_void_p, POINTER(c_ulong), \

  POINTER(ErrInf)]

SAP.RfcGetRowCount.restype = c_ulong

 

SAP.RfcGetStructure.argtypes = [c_void_p, c_wchar_p, \

  POINTER(c_void_p), POINTER(ErrInf)]

SAP.RfcGetStructure.restype = c_ulong

 

SAP.RfcGetTable.argtypes = [c_void_p, c_wchar_p, POINTER(c_void_p), \

  POINTER(ErrInf)]

SAP.RfcGetTable.restype = c_ulong

 

SAP.RfcGetVersion.argtypes = [POINTER(c_ulong), POINTER(c_ulong), \

  POINTER(c_ulong)]

SAP.RfcGetVersion.restype = c_wchar_p

 

SAP.RfcInstallServerFunction.argtypes = [c_wchar_p, c_void_p, \

  c_void_p, POINTER(ErrInf)]

SAP.RfcInstallServerFunction.restype = c_ulong

 

SAP.RfcInvoke.argtypes = [c_void_p, c_void_p, POINTER(ErrInf)]

SAP.RfcInvoke.restype = c_ulong

 

SAP.RfcListenAndDispatch.argtypes = [c_void_p, c_ulong, POINTER(ErrInf)]

SAP.RfcListenAndDispatch.restype = c_ulong

 

SAP.RfcMoveToFirstRow.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcMoveToFirstRow.restype = c_ulong

 

SAP.RfcMoveToNextRow.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcMoveToNextRow.restype = c_ulong

 

SAP.RfcOpenConnection.argtypes = [POINTER(ConnParams), c_ulong, \

  POINTER(ErrInf)]

SAP.RfcOpenConnection.restype = c_void_p

 

SAP.RfcPing.argtypes = [c_void_p, POINTER(ErrInf)]

SAP.RfcPing.restype = c_ulong

 

SAP.RfcRegisterServer.argtypes = [POINTER(SConParams), c_ulong, \

  POINTER(ErrInf)]

SAP.RfcRegisterServer.restype = c_void_p

 

SAP.RfcSetChars.argtypes = [c_void_p, c_wchar_p, c_wchar_p, c_ulong, \

  POINTER(ErrInf)]

SAP.RfcSetChars.restype = c_ulong

 

#-End-------------------------------------------------------------------

Python for Basis (Part 2): Scheduling SUIM Jobs using Python

$
0
0

In this post I will document how to schedule batch jobs using python and how to handle the output. You may want to check out my first blog post as an introduction on how to access SAP through Python: Python for Basis (Part 1) .

 

Description of the Scenario

In the following section we want to perform a security assessment by scheduling report RSUSR002 to find out the following:

 

  • Which users have SAP_ALL?
  • Which users are able to execute any function module thanks to S_DEVELOP?

 

That means, we need to accomplish the following tasks:

 

  • Logon to the SAP client we want to evaluate
  • Create a variant for report RSUSR002
  • Schedule report RSUSR002 in the background with immediate start
  • Wait for completion of the job
  • Download the generated spool
  • Extract the users from the downloaded spool
  • Generate an Excel file with the extracted information

 

Extending the PyRFC Connection Class

The easiest way to accomplish our task is to create a subclass of the PyRFC Connection class and add our own functions to it.

 

import pyrfc

class sap_abapclient(pyrfc.Connection):

    def __init__(self, *args, **kwargs ):

        pyrfc.Connection.__init__(self, *args, **kwargs )

        self.condetails=self.get_connection_attributes()

 

Instead of using the standard pyrfc.Connection class to connect to an SAP system we will use our own class. The following sections will document the different functions required to complete the task.

 

Check whether RSUSR002 variant already exists

Before we schedule a RSUSR002 job we need to create or modify a corresponding variant. Since we need to use different function modules for maintenance and creation, we need to check first, whether a variant exists. The following function will take care of this:

 

    def check_if_variant_exists(self, program, variant):

        result = self.call('RFC_READ_TABLE',

                         QUERY_TABLE='VARID', \

                         DELIMITER='|', \

                         FIELDS=[{'FIELDNAME':'VARIANT'}], \

                         OPTIONS = [{'TEXT':"REPORT EQ '" + program.upper()+"'"}])

 

        tabledump=result['DATA']

        variant_exists=False

        for record in tabledump:

            if record['WA']==variant.upper():

                variant_exists=True

        return(variant_exists)

 

The function performs the following:

 

  • Execute function module RFC_READ_TABLE to download the existing variant names of report RSUSR002
  • Initialize the boolean variable variant_exists
  • loop over all records and find out if the variant name we picked already exists.
  • If the variant exists, variant_exists will be set to True
  • At the end the function returns the content of the variant_exists.

 

Create/Maintain RSUSR002 variant

To create a variant we use RS_CREATE_VARIANT_RFC, to modify an existing variant we use RS_CHANGE_CREATED_VARIANT_RFC. The corresponding python function starts with an underscore ('_') which by convention determines that a function should not be called directly. It would be a private method but python doesn't prevent calling these functions directly.

 

If you look at the definition of the function modules then you see several different import parameters. We need the following:

 

  • VARI_DESC: Defines the variant name and will contain the change date and timestamp
  • VARI_CONTENTS: contains the actual variant values
  • VARI_TEXT: Contains the description in the variant attributes

 

VARI_CONTENTS is defined as a table with the following fields that are part of the variant values table when looking at the variant through transaction SE38:

  • SELNAME: Selection Screen
  • KIND: Type
  • OPTION: Option
  • LOW: frm
  • HIGH: to
  • SIGN: I/E

 

The easiest way to get an idea of the variants is to define it in the report itself and then look at the actual values using the variant editor in transaction SE38. Like with all tables, the variant has to be defined as a list of dictionaries. This is the python function to create a create or maintain a variant for RSUSR002:

 

    def _create_suim_variant(self, variant):

        """

        Creates a temporary variant to schedule the report

        :param variant: list of dictionaries that comprise the variant content.

 

        """

        import datetime

        report = u'RSUSR002'

        variantname = u'TEMPVARI'

        vari_text = list()

        vari_list = list()

        vari_contents = list()

        vscreens = list()

 

        temp = dict(MANDT = self.condetails['client'],

                     LANGU=u'EN',

                     REPORT=report,

                     VARIANT=variantname,

                     VTEXT=u'temporary')

 

        vari_text.append(temp)

 

        vari_desc = dict(REPORT = report,

                         VARIANT=  variantname,

                         AEDAT = datetime.date(2013,12,12),

                         AETIME = datetime.time(00,00))

 

        vari_contents.extend(variant)

 

        if self.check_if_variant_exists(report, variantname)==True:

            test = self.call('RS_CHANGE_CREATED_VARIANT_RFC', CURR_REPORT= report,

                                                              CURR_VARIANT= variantname,

                                                              VARI_DESC = vari_desc,

                                                              VARI_CONTENTS = vari_contents,

                                                              VARI_TEXT = vari_text)

        else:

            test = self.call('RS_CREATE_VARIANT_RFC', CURR_REPORT= report,

                                                      CURR_VARIANT= variantname,

                                                      VARI_DESC = vari_desc,

                                                      VARI_CONTENTS = vari_contents,

                                                      VARI_TEXT = vari_text)

 

Schedule report RSUSR002 as Batch Job

To schedule the report in background we will to use the XBP interface (Background Processing, Job Scheduling (BC-XBP). we are going to run the following function modules:

 

  • BAPI_XMI_LOGON: Before we schedule a job we need to register with the instance
  • BAPI_XBP_JOB_OPEN: Initiate the creation of a new job definition
  • BAPI_XBP_JOB_ADD_ABAP_STEP: Define the job step to execute
  • BAPI_XBP_JOB_CLOSE: complete the job creation
  • BAPI_XBP_JOB_START_IMMEDIATELY: Change the job definition for immediate start.
  • BAPI_XMI_LOGOFF: log off from the instance

 

Here is the code:

 

    def schedule_job_immediately(self, jobname, program, variant='none', wait='yes'):

        import datetime

 

        if variant<>'none':

            if self.check_if_variant_exists(program, variant)==False:

                print('Variant Does not Exist')

                exit(3)

 

        result = self.call ('BAPI_XMI_LOGON', EXTCOMPANY='LARS',

                                              EXTPRODUCT='assessment',

                                              INTERFACE='XBP',

                                              VERSION='2.0')

 

        result = self.call('BAPI_XBP_JOB_OPEN', JOBNAME=jobname,

                                                EXTERNAL_USER_NAME='AUDIT')


        jobcount=result['JOBCOUNT']

 

        if variant<>'none':

            result = self.call('BAPI_XBP_JOB_ADD_ABAP_STEP', JOBNAME=jobname,

                                                             JOBCOUNT=jobcount,

                                                             EXTERNAL_USER_NAME='AUDIT',

                                                             ABAP_PROGRAM_NAME=program,

                                                             ABAP_VARIANT_NAME=variant)

        else:

            result = self.call('BAPI_XBP_JOB_ADD_ABAP_STEP', JOBNAME=jobname,

                                                             JOBCOUNT=jobcount,

                                                             EXTERNAL_USER_NAME='AUDIT',

                                                             ABAP_PROGRAM_NAME=program)

 

        result = self.call('BAPI_XBP_JOB_CLOSE', JOBNAME=jobname,

                                                 JOBCOUNT=jobcount,

                                                 EXTERNAL_USER_NAME='AUDIT')

 

        result = self.call('BAPI_XBP_JOB_START_IMMEDIATELY', JOBNAME=jobname,

                                                             JOBCOUNT=jobcount,

                                                             EXTERNAL_USER_NAME='AUDIT')

 

        if wait=='yes':

            jobstatus=self._wait_until_job_completed(jobname,jobcount)

 

        result=dict(JOBCOUNT  = jobcount,

                    JOBSTATUS = jobstatus)

        return(result)

 

 

Wait until Job Completes

The job was started immediately, but before we can download the spool, we need to make sure that the job is really complete. We will use function module SUBST_CHECK_BATCHJOB for this:

 

    def _wait_until_job_completed(self, jobname, jobcount):

        """

        Checks whether a job is still running and waits until it completes.

        :param jobname:

        :param jobcount:

        """

        jobstatus = 'X'

        while jobstatus not in ['F','A']:

            status=self.call('SUBST_CHECK_BATCHJOB',JOBNAME=jobname, JOBCOUNT=jobcount)

            jobstatus=status['JOBSTATUS']

            print ('job running')

            time.sleep(3)

        return(jobstatus)

 

Determine the Spool-ID

The batch job will generate a spool request that is identified by the spool ID. To determine the spool ID we need to check table TBTCP:

 

 

    def determine_spool_id(self, jobname, jobcount):

        """

        find the spool ID based on the job name and job count.

        :param jobname: name of the SM37 job

        :param jobcount: Job Count

        :return:

        """


        where_clause = "JOBNAME EQ '" +jobname+"' AND JOBCOUNT EQ '" + jobcount + "' AND STEPCOUNT EQ '1'"

 

        result = self.call('RFC_READ_TABLE', \

                           QUERY_TABLE='TBTCP', \

                           DELIMITER='|', \

                           FIELDS=[{'FIELDNAME':'LISTIDENT'}], \

                           OPTIONS = [{'TEXT':where_clause}])

 

        tabledump=result['DATA']

        spoolid = tabledump[0]['WA']

        return spoolid

 

Download Spool

After finding out the spool ID we can download the spool into a variable for further analysis:

 

    def download_spool(self, spoolid):

        """

        Download Spool File

        :param spoolid: spool ID of the job

        """

        result = self.call ('BAPI_XMI_LOGON', EXTCOMPANY='linkies',

                                              EXTPRODUCT='assessment',

                                              INTERFACE='XBP',

                                              VERSION='2.0')

        result = self.call('BAPI_XBP_JOB_READ_SINGLE_SPOOL',SPOOL_REQUEST=spoolid,

                                                            EXTERNAL_USER_NAME='AUDIT'

                                                            )

        return(result['SPOOL_LIST_PLAIN'])

 

Extract Users From Spool

The spool file will be a large text stored in a variable. We need to parse the spool file to extract the user information:

 

    def _extract_users_from_spool(self, spoolfile):

        """

        Extracts the users from the spool file generated by SUIM

        :param spoolfile:

        :return: returns a list of dictionaries with the user records.

        """

        import re

        users = list()

        issues = list()

        userrecord = dict()

        for line in spoolfile:

            flatline = line['LINE']

            if flatline<>'':

                if flatline[0]=='|' and flatline[0:7] == '|  User':  

                    linearray=self.locations_of_substring(flatline,'|')

                if flatline[0]=='|' and flatline[0:9] <>'|  User  ' \

                                    and flatline[0:9] <>'|--------' \

                                    and flatline[0:9] <>'|  User n':

                    userrecord=dict(username     = flatline[1:linearray[1]].strip(),

                                    completename = flatline[linearray[1]+1:linearray[2]].strip(),

                                    usergroup    = flatline[linearray[2]+1:linearray[3]].strip(),

                                    accountnumber= flatline[linearray[3]+1:linearray[4]].strip(),

                                    locked       = flatline[linearray[4]+1:linearray[5]].strip(),

                                    reason       = flatline[linearray[5]+1:linearray[6]].strip(),

                                    validfrom    = flatline[linearray[6]+1:linearray[7]].strip(),

                                    validto      = flatline[linearray[7]+1:linearray[8]].strip(),

                                    usertype     = flatline[linearray[8]+1:linearray[9]].strip(),

                                    refuser      = flatline[linearray[9]+1:linearray[10]].strip())

                    if len(linearray)==12:

                        # there is a new policy column with 7.31. That needs to be included

                        userrecord['policy'] = flatline[linearray[10]+1:linearray[11]].strip()

                    if userrecord['reason'] <>'':

                        userrecord['locked']='X'

                    users.append(userrecord)

        return(users)

 

The function uses another function to determine the number of '|' in the first line that contains the headers. We need to add that function to our class, too:

 

    def locations_of_substring(self, string, substring):

        """Return a list of locations of a substring.

        :param substring:

        """

        substring_length = len(substring)

        def recurse(locations_found, start):

            location = string.find(substring, start)

            if location != -1:

                return recurse(locations_found + [location], location+substring_length)

            else:

                return locations_found

        return recurse([], 0)

 

Generate the Excel file with the users

To generate the excel file I will use a different python module that needs to be installed called xlsxwriter (XlsxWriter 0.5.7 : Python Package Index).

 

    def generate_xlsx_file(self, data, filename):

        """

        Generate a XLSX file using returned data

        :param data: data

        aram filename: xlsx filename

        """

 

        import xlsxwriter

        workbook = xlsxwriter.Workbook(filename)

        worksheet = workbook.add_worksheet()

        worksheet.freeze_panes(1,0)

 

        xls_row=0

        xls_column=0

 

        bold = workbook.add_format({'bold': True})

 

        for record in data:

            if len(record.keys())==10:

                if xls_row==0:

                    for i in range(0,9):

                        worksheet.write(xls_row,i,self.columns_10[i],bold)

                        worksheet.set_column(0,0,20)

                        worksheet.set_column(1,1,50)

                    xls_row=1

                else:

                    for i in range(0,9):

                        if i == 0: worksheet.write(xls_row,i,  data['username'])

                        elif i == 1: worksheet.write(xls_row,i,data['completename'])

                        elif i == 2: worksheet.write(xls_row,i,data['usergroup'])

                        elif i == 3: worksheet.write(xls_row,i,data['accountnumber'])

                        elif i == 4: worksheet.write(xls_row,i,data['locked'])

                        elif i == 5: worksheet.write(xls_row,i,data['reason'])

                        elif i == 6: worksheet.write(xls_row,i,data['validfrom'])

                        elif i == 7: worksheet.write(xls_row,i,data['validto'])

                        elif i == 8: worksheet.write(xls_row,i,data['usertype'])

                        elif i == 9: worksheet.write(xls_row,i,data['refuser'])

                    xls_row=xls_row+1

 

            elif len(record.keys())==11:

                if xls_row==0:

                    for i in range(0,10):

                        worksheet.write(0,i,self.columns_11[i],bold)

                        worksheet.set_column(0,0,20)

                        worksheet.set_column(1,1,50)

                    xls_row=1

                else:

                    for i in range(0,10):

                        if   i == 0: worksheet.write(xls_row, i,record['username'])

                        elif i == 1: worksheet.write(xls_row, i,record['completename'])

                        elif i == 2: worksheet.write(xls_row, i,record['usergroup'])

                        elif i == 3: worksheet.write(xls_row, i,record['accountnumber'])

                        elif i == 4: worksheet.write(xls_row, i,record['locked'])

                        elif i == 5: worksheet.write(xls_row, i,record['reason'])

                        elif i == 6: worksheet.write(xls_row, i,record['validfrom'])

                        elif i == 7: worksheet.write(xls_row, i,record['validto'])

                        elif i == 8: worksheet.write(xls_row, i,record['usertype'])

                        elif i == 9: worksheet.write(xls_row, i,record['refuser'])

                        elif i == 10: worksheet.write(xls_row,i,record['policy'])

                    xls_row=xls_row+1

        workbook.close()

 

Combining all functions

The last function will perform all of the previous functions to make the scheduling easier.

 

    def run_suim_job(self,variant, filename):

        """

        run a SUIM report using a single variant

        :param variant: dictionary that contains the variant

        :return:

        """

        self._create_suim_variant(variant)

        result=self.schedule_job_immediately('RSUSR002','RSUSR002','TEMPVARI')

        spoolid=self.determine_spool_id('RSUSR002',result['JOBCOUNT'])

        spoolfile = self.download_spool(int(spoolid))

        users = self._extract_users_from_spool(spoolfile)

        self.generate_xlsx_file(self, users, filename)

 

Putting it all together

 

The entire class would look like this:

 

 

import pyrfc, xlsxwriter

class sap_abapclient(pyrfc.Connection):

    def __init__(self, *args, **kwargs ):

        pyrfc.Connection.__init__(self, *args, **kwargs )

        self.condetails=self.get_connection_attributes()



    def run_suim_job(self,variant, filename):

         ...


    def def generate_xlsx_file(self, data, filename):

         ...


    def locations_of_substring(self, string, substring):

         ...

 

    def _extract_users_from_spool(self, spoolfile):

         ...


    def download_spool(self, spoolid):

         ...


    def schedule_job_immediately(self, jobname, program, variant='none', wait='yes'):

          ...

 

    def check_if_variant_exists(self, program, variant):

          ...


    def _create_suim_variant(self, variant):

         ...


    def _wait_until_job_completed(self, jobname, jobcount):

         ...


    def determine_spool_id(self, jobname, jobcount):

         ...


    def download_spool(self, spoolid):

         ...


Executing the Script

To test the script we need to add some lines to the end of the file to make it actually do something. Otherwise the file would only be a text file that contains a definition of a python class but nothing else. The following construct will execute the script.

 

  • the IF condition will execute the corresponding code underneath
  • several variables will be initialized
  • a connection to the SAP system is initiated using our own subclass that inherited everything from the pyrfc.Connection class.
  • our own functions to schedule RSUSR002 will be executed.

 

If everything works well, we will have two Excel files in our current work directory. The example does not contain any error handling though.

 

if __name__ == '__main__':

     sap_all_variant=[]

        # the next lines contain the data for the variant as stored in parameters.

        tempdict=dict(SELNAME = "PROF1", KIND = "S", OPTION = "EQ", LOW = "SAP_ALL", HIGH = "", SIGN = "I")

        self.variantdict.append(tempdict)


    s_develop_variant=[]

        tempdict=dict(SELNAME = "OBJ1",   KIND = "P",  OPTION = "", LOW = "S_DEVELOP",  HIGH = "", SIGN = "")

        self.variantdict.append(tempdict)

        tempdict=dict(SELNAME = "VAL111", KIND = "P",  OPTION = "", LOW = 'PROG',    HIGH = "", SIGN = "")

        self.variantdict.append(tempdict)

        tempdict=dict(SELNAME = "VAL141", KIND = "P",  OPTION = "", LOW = '03',      HIGH = "", SIGN = "")

        self.variantdict.append(tempdict)


    logon_details = dict( user = <username>,

                          passwd = <secret>,

                         ashost = <hostname>,

                          client = <client>,

                          sysnr = <systemnumber>,

                         sid = <sid> )


    connection=sap_abapclient(**params)

    connection.run_suim_job(sap_all_variant,'sap_all_users.xlsx')

    connection.run_suim_job(s_develop_variant,'s_develop_users.xlsx')


 

This script can be easily extended. You don't need to write things into a excel spreadsheet. You can store things in a database, refresh the data regularly and send an email with changes for example... or generate a nice HTML page using a template engine like jinja2 (Welcome | Jinja2 (The Python Template Engine)), Or you can use the example and write a script that schedules all kinds of basis jobs consistently across an SAP landscape....


SAP GUI script automation my experience

$
0
0

Activity

Automation SAP mass activities

Technology used

SAP GUI scripting

Method of working

Record your activity in SAP, add custom variables, Loop it to do it repeatedly by reading

Values from excel sheet

Comparative benefits

There is no other tool in SAP which can automate activities partially. Portability is best in SAPGUI scripting . SAP GUI Partially automate a sequence of repeated activity. For example role creation , how to automate in such activities where consultant need to interfere in between and provide some inputs in system ? What if such automating scripts can be stored and executed from your desktop? This makes your automation one time activity for your entire lifetime. You can apply such scripts in any SAP system , you switch client, you switch systems, you switch network , you switch SAP products- doesn't matter.

Successful activities

Mass user creation fully automated, Table updates fully automated, Role building partially automated

Successful project

An implementation was done with just one Security consultant.

Required non SAP Skill

To execute scripts just SAP knowledge is enough. To create script a little bit of VBScript understanding and aptitude for programming required. Excel knowledge with Vlookup is added advatage.

 

How To Use tRFC/qRFC Calls With VBScript Via CCo

$
0
0

Hello community,

 

I introduced COM Connector (CCo) here. Here now an example how to use transactional RFC calls with VBScript via CCo. The queued RFC calls are commented. You can find more information about the different RFC variants here.

 

'-Begin-----------------------------------------------------------------

 

  '-Directives----------------------------------------------------------

    Option Explicit

 

  '-Constants-----------------------------------------------------------

    Const RFC_OK = 0

 

  '-Sub Main------------------------------------------------------------

    Sub Main()

 

      '-Variables-------------------------------------------------------

        Dim SAP, hRFC, rc, TID, hTrans, hFuncDesc, hFunc

 

      Set SAP = CreateObject("COMNWRFC")

      If Not IsObject(SAP) Then

        Exit Sub

      End If

 

      hRFC = SAP.RfcOpenConnection("ASHOST=ABAP, SYSNR=00, " & _

        "CLIENT=001, USER=BCUSER")

      If hRFC = 0 Then

        Set SAP = Nothing

        Exit Sub

      End If

 

      hFuncDesc = SAP.RfcGetFunctionDesc(hRFC, "RFC_PING")

      If hFuncDesc = 0 Then

        rc = SAP.RfcCloseConnection(hRFC)

        Set SAP = Nothing

        Exit Sub

      End If

 

      hFunc = SAP.RfcCreateFunction(hFuncDesc)

      If hFunc = 0 Then

        rc = SAP.RfcCloseConnection(hRFC)

        Set SAP = Nothing

        Exit Sub

      End If

 

      If SAP.RfcGetTransactionID(hRFC, TID) <> RFC_OK Then

        rc = SAP.RfcCloseConnection(hRFC)

        Set SAP = Nothing

        Exit Sub

      End If

 

      'qRFC

      'hTrans = SAP.RfcCreateTransaction(hRFC, TID, "StefansQueue")

 

      hTrans = SAP.RfcCreateTransaction(hRFC, TID, "")

      If hTrans = 0 Then

        rc = SAP.RfcCloseConnection(hRFC)

        Set SAP = Nothing

        Exit Sub

      End If

 

      If SAP.RfcInvokeInTransaction(hTrans, hFunc) = RFC_OK Then

 

        If SAP.RfcSubmitTransaction(hTrans) = RFC_OK Then

 

          'qRFC

          'MsgBox "Look in TAC SMQ2 (Inbound Queue)"

 

          MsgBox "Look in table ARFCRSTATE for TID " & TID

 

          rc = SAP.RfcConfirmTransaction(hTrans)

 

        End If

 

      End If

 

      rc = SAP.RfcDestroyTransaction(hTrans)

      rc = SAP.RfcDestroyFunction(hFunc)

      rc = SAP.RfcCloseConnection(hRFC)

      Set SAP = Nothing

 

    End Sub

 

  '-Main----------------------------------------------------------------

    Main()

 

'-End-------------------------------------------------------------------

 

Enjoy it.

 

Cheers

Stefan

How To Use bgRFC Calls With VBScript Via CCo

$
0
0

Hello community,

 

I introduced COM Connector (CCo) here. Here now an example how to use background RFC calls with VBScript via CCo. The queued RFC calls are commented. You can find more information about the different RFC variants here. I presented the possibility to use transactional (tRFC) and queued (qRFC) RFC calls here.

 

'-Begin-----------------------------------------------------------------

'-

'- Look also in TAC SBGRFCCONF and SBGRFCMON

'- Hint: You need SAP 7.0 EHP 1 for SAP NetWeaver 7.0 Support Package 4

'-       and higher

'-

'-----------------------------------------------------------------------

 

  '-Directives----------------------------------------------------------

    Option Explicit

 

  '-Constants-----------------------------------------------------------

    Const RFC_OK = 0

 

  '-Sub Main------------------------------------------------------------

    Sub Main()

 

      '-Variables-------------------------------------------------------

        Dim SAP, CCo, hRFC, rc, UID, hUnit, hFuncDesc, hFunc

        Dim unitAttrID, unitAttr, unitIdentID, unitIdent

 

      Set SAP = CreateObject("COMNWRFC")

      If Not IsObject(SAP) Then

        Exit Sub

      End If

 

      Set CCo = CreateObject("COMNWRFCHELP")

      If Not IsObject(CCo) Then

        Exit Sub

      End If

 

      hRFC = SAP.RfcOpenConnection("ASHOST=ABAP, SYSNR=00, " & _

        "CLIENT=001, USER=BCUSER")

      If hRFC = 0 Then

        Set SAP = Nothing

        Set CCo = Nothing

        Exit Sub

      End If

 

      hFuncDesc = SAP.RfcGetFunctionDesc(hRFC, "RFC_PING")

      If hFuncDesc = 0 Then

        rc = SAP.RfcCloseConnection(hRFC)

        Set SAP = Nothing

        Set CCo = Nothing

        Exit Sub

      End If

 

      hFunc = SAP.RfcCreateFunction(hFuncDesc)

      If hFunc = 0 Then

        rc = SAP.RfcCloseConnection(hRFC)

        Set SAP = Nothing

        Set CCo = Nothing

        Exit Sub

      End If

 

      If SAP.RfcGetUnitID(hRFC, UID) <> RFC_OK Then

        rc = SAP.RfcCloseConnection(hRFC)

        Set SAP = Nothing

        Set CCo = Nothing

        Exit Sub

      End If

 

      '-Create structures RFC_UNIT_ATTRIBUTES and RFC_UNIT_IDENTIFIER---

        If CCo.AllocateMemory(273, unitAttrID, unitAttr) = vbFalse Or _

          CCo.AllocateMemory(70, unitIdentID, unitIdent) = vbFalse Then

          rc = SAP.RfcCloseConnection(hRFC)

          Set SAP = Nothing

          Set CCo = Nothing

          Exit Sub

        End If

 

      '-qRFC------------------------------------------------------------

'       hUnit = SAP.RfcCreateUnit(hRFC, UID, "STEFANSQUEUE", unitAttr, _

'         unitIdent)

 

      '-tRFC------------------------------------------------------------

        hUnit = SAP.RfcCreateUnit(hRFC, UID, "", unitAttr, unitIdent)

        If hUnit = 0 Then

          rc = SAP.RfcCloseConnection(hRFC)

          Set SAP = Nothing

          rc = CCo.FreeMemory(unitIdentID)

          rc = CCo.FreeMemory(unitAttrID)

          Set CCo = Nothing

          Exit Sub

        End If

 

      If SAP.RfcInvokeInUnit(hUnit, hFunc) = RFC_OK Then

 

        If SAP.RfcSubmitUnit(hUnit) = RFC_OK Then

 

          MsgBox "Look in table BGRFC_SRV_STATE for UID " & UID

 

          SAP.ErrorMsgTarget = 2

          If SAP.RfcConfirmUnit(hRFC, unitIdent) = RFC_OK Then

            MsgBox "Look in table BGRFC_SRV_CFM for UID " & UID

          Else

            MsgBox SAP.ErrorMessage

          End If

 

        End If

 

      End If

 

      rc = SAP.RfcDestroyUnit(hUnit)

      rc = SAP.RfcDestroyFunction(hFunc)

      rc = SAP.RfcCloseConnection(hRFC)

      Set SAP = Nothing

      rc = CCo.FreeMemory(unitIdentID)

      rc = CCo.FreeMemory(unitAttrID)

      Set CCo = Nothing

 

    End Sub

 

  '-Main----------------------------------------------------------------

    Main()

 

'-End-------------------------------------------------------------------

 

Background RFCs are the recommended kind of RFCs.You should never use a synchronous RFC to write data into an SAP system, only to read data. To write data use tRFC or qRFC, or better the bgRFC. With this examples here and here you have now the easy possibility to use any kind of RFC with your favorite scripting language.

 

Enjoy it.

 

Cheers

Stefan

UI5 / HTML5 UI Generators

$
0
0

Reading the Raymond Ni blog on generating UI forms from JSON script, I combined this "cool hack with potential" with Python scripting, to read ABAP metadata from the backend system and generate UI5 or HTML5 form, like this one here, automatically.

 

This can save the prototyping time because the developer does not have to start from scratch but from automatically generated form HTML fragment.

 

The source code can be downloaded from bsrdjan/formgen · GitHub

 

Form Metadata

 

Let take one complex form as example and assume that selection screen of Timesheets approval transaction CAPS, for some reason shall be implemented in UI5 or HTML5:

CAPS.png

Using F1 HELP in ABAP Workbench you may find out technical types of input field of this selection screen and copy your findings in one JSON or YAML file, like below. For the purpose of this blog I used YAML, just to put few comments in, but you can remove them and save-as JSON.

 

form.yaml

 

{

    #

    # CAPS Selection

    #

    "form": [

        { "FIELD": ["PERNR", "PERNR"], "SHLP": "PREM" }, # Personnel Number

        { "FIELD": ["PERNR", "STAT2"]},   # Employment status

        { "FIELD": ["PERNR", "PERSG"]},   # Employment group

        { "FIELD": ["PERNR", "PERSK"]},   # Employee subgroup

        { "FIELD": ["PERNR", "VDSK1"]},   # Organization ke

        { "FIELD": ["PERNR", "ABKRS"]},   # Payroll area

        { "FIELD": ["PERNR", "KOKRS"]},   # Controlling area

        { "FIELD": ["PERNR", "KOSTL"]},   # Cost center

        { "FIELD": ["PERNR", "ORGEH"]},   # Organizational unit

        { "FIELD": ["PERNR", "MSTBR"]},   # Supervisor area

        { "FIELD": ["PERNR", "SBMOD"]},   # Administrator group

        { "FIELD": ["PERNR", "SACHZ"]},   # Time recording administrator

        { "FIELD": ["PERNR", "SASBZ"]}    # Administrator group: time

    ]

}


To process this form you can write a simple nodejs or Python script, like this one.

 

UI5 Form

 

ui5form.py

 

 

#!/usr/bin/python

 

# connect to abap

 

from pyrfc import *

 

def get_connection (conn):

   """ get Connection"""

   print 'connecting %s ...' % conn['ashost']

   return Connection(**conn)

 

I64 = {

  'user': 'demo',

  'passwd': 'welcome',

  'ashost': '10.11.12.12',

  'sysnr': '00',

  'client': '800',

  'saprouter': ''

}

 

c = get_connection(I64)

 

# read form fields

 

import yaml

from pprint import pprint

with open('form.yaml', 'r') as data_file:

    form_fields = yaml.load(data_file)["form"]

 

# make ui5 form

 

# header

 

print '''

var oLayout1 = new sap.ui.layout.form.GridLayout();

var oLayout2 = new sap.ui.layout.form.ResponsiveLayout();

var oLayout3 = new sap.ui.layout.form.ResponsiveGridLayout();

 

var oForm1 = new sap.ui.layout.form.Form("F1",{

    title: new sap.ui.core.Title({text: "Time Approval", tooltip: "Time Approval tooltip"}),

    layout: oLayout1,

    formContainers: [

        new sap.ui.layout.form.FormContainer("F1C1",{

            title: "Time Approval",

            formElements: [

'''

 

for _line in form_fields:

    field = _line['FIELD']

    r = c.call('DDIF_FIELDINFO_GET',

                TABNAME=field[0], FIELDNAME=field[1],

                ALL_TYPES='X',    GROUP_NAMES='X' )

    dfies=r['DFIES_TAB'][0]

 

 

    print '''

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"%s",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "%s", maxLength: "%d",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),'''  % (dfies['SCRTEXT_L'], '', int(dfies['LENG']))

 

 

    print

    print

 

 

# footer

 

print '''

        ]

    });

oForm1.placeAt("sample1");

'''

 

Both Python and nodejs can directly call ABAP RFCs (see PyRFC and nodeRFC) and you can use the language you like more.

 

After running this script and redirecting the output to ui5form.html, the following fragment is generated

 

ui5form.html

 

connecting 10.117.24.158 ...

 

var oLayout1 = new sap.ui.layout.form.GridLayout();

var oLayout2 = new sap.ui.layout.form.ResponsiveLayout();

var oLayout3 = new sap.ui.layout.form.ResponsiveGridLayout();

 

 

var oForm1 = new sap.ui.layout.form.Form("F1",{

    title: new sap.ui.core.Title({text: "Time Approval", tooltip: "Time Approval tooltip"}),

    layout: oLayout1,

    formContainers: [

        new sap.ui.layout.form.FormContainer("F1C1",{

            title: "Time Approval",

            formElements: [

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Personnel Number",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "8",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Employment status",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "1",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Employee group",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "1",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Employee subgroup",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "2",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Organizational key",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "14",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Payroll area",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "2",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Controlling Area",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "4",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Cost Center",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "10",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Organizational unit",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "8",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Supervisor area",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "8",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Administrator group",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "4",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Time administrator",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "3",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        new sap.ui.layout.form.FormElement({

                label: new sap.ui.commons.Label({text:"Admin.modif, time admin.",

                        layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 1}),

                                                     new sap.ui.layout.form.GridElementData({hCells: "1"}),

                                                     new sap.ui.layout.GridData({span: "L1 M1 S12"})]

                                })

                        }),

                fields: [new sap.ui.commons.TextField({value: "", maxLength: "7",

                            layoutData: new sap.ui.core.VariantLayoutData({

                                multipleLayoutData: [new sap.ui.layout.ResponsiveFlowLayoutData({weight: 4}),

                                                         new sap.ui.layout.GridData({span: "L5 M5 S12"})]

                            })

                         })

                        ],

                layoutData: new sap.ui.layout.ResponsiveFlowLayoutData({linebreak: true, margin: false})

        }),

 

 

        ]

    });

oForm1.placeAt("sample1");

 

 

The similarity with UI5 form example is obvious, this template was used in this script example. From ABAP metadata I extracted the label text, input field type and max length, but perhaps more is possible. Some manual work still needed but not like starting from the blank sheet of paper.

 

HTML5 Form

 

With slightly modified logic, the HTML5 form can be generated. The section "make ui5 form" shall be replaced with the code below.

 

html5form.py

 

 

# make html5 form

 

# header

 

print '<form name="Time Approval" novalidate role="form">'

 

for _line in form_fields:

    field = _line['FIELD']

    r = c.call('DDIF_FIELDINFO_GET',

                    TABNAME=field[0], FIELDNAME=field[1],

                    ALL_TYPES='X',    GROUP_NAMES='X' )

    dfies=r['DFIES_TAB'][0]

    fname = 'name_' + field[1]

    if dfies['DATATYPE'] == 'NUMC':

        ftype = 'number'

    else:

        ftype = 'text'

    print '''

    <div class="form-group">

      <label for="%s">%s</label>

      <input type="%s" name="%s" class="form-control" required maxLength=%d />

    </div>''' % (fname, dfies['SCRTEXT_L'], ftype, fname, int(dfies['LENG']))

    print

 

# footer

 

print '</form>'

 

 

html5form.html

 

<form name="Time Approval" novalidate role="form">

 

 

    <div class="form-group">

      <label for="name_PERNR">Personnel Number</label>

      <input type="number" name="name_PERNR" class="form-control" required maxLength=8 />

    </div>

 

 

    <div class="form-group">

      <label for="name_STAT2">Employment status</label>

      <input type="text" name="name_STAT2" class="form-control" required maxLength=1 />

    </div>

 

 

    <div class="form-group">

      <label for="name_PERSG">Employee group</label>

      <input type="text" name="name_PERSG" class="form-control" required maxLength=1 />

    </div>

 

 

    <div class="form-group">

      <label for="name_PERSK">Employee subgroup</label>

      <input type="text" name="name_PERSK" class="form-control" required maxLength=2 />

    </div>

 

 

    <div class="form-group">

      <label for="name_VDSK1">Organizational key</label>

      <input type="text" name="name_VDSK1" class="form-control" required maxLength=14 />

    </div>

 

 

    <div class="form-group">

      <label for="name_ABKRS">Payroll area</label>

      <input type="text" name="name_ABKRS" class="form-control" required maxLength=2 />

    </div>

 

 

    <div class="form-group">

      <label for="name_KOKRS">Controlling Area</label>

      <input type="text" name="name_KOKRS" class="form-control" required maxLength=4 />

    </div>

 

 

    <div class="form-group">

      <label for="name_KOSTL">Cost Center</label>

      <input type="text" name="name_KOSTL" class="form-control" required maxLength=10 />

    </div>

 

 

    <div class="form-group">

      <label for="name_ORGEH">Organizational unit</label>

      <input type="number" name="name_ORGEH" class="form-control" required maxLength=8 />

    </div>

 

 

    <div class="form-group">

      <label for="name_MSTBR">Supervisor area</label>

      <input type="text" name="name_MSTBR" class="form-control" required maxLength=8 />

    </div>

 

 

    <div class="form-group">

      <label for="name_SBMOD">Administrator group</label>

      <input type="text" name="name_SBMOD" class="form-control" required maxLength=4 />

    </div>

 

 

    <div class="form-group">

      <label for="name_SACHZ">Time administrator</label>

      <input type="text" name="name_SACHZ" class="form-control" required maxLength=3 />

    </div>

 

 

    <div class="form-group">

      <label for="name_SASBZ">Admin.modif, time admin.</label>

      <input type="text" name="name_SASBZ" class="form-control" required maxLength=7 />

    </div>

 

</form>

 

And here the generated form, incorporated into HTML document:

 

html5.png

 

Hope this adds potential to the hack and helps saving cloud/web prototyping time, also find out if more info can be extracted from ABAP metadata and mapped to HTML5 and UI5 input attributes

VBScript is Not Longer Supported in IE11, Run VBScript from JavaScript

$
0
0

Hello community,

 

it seems that the time for VBScript is running out, you can find here an information in the Internet Explorer Dev Center about the deprecation of VBScript in the context of IE 11 in edge mode,

 

However, that is no reason to hang the ears 

 

Here a function in JavaScript to call a VBScript from IE 11 in edge mode:

 

function callVBS(scriptName) {


  //-Variables--------------------------------------------------------

    var wsh, pathName;

 

 

  if ("ActiveXObject" in window) {

    wsh = new ActiveXObject("WScript.Shell");

    if (typeof(wsh) == 'object') {

      pathName = location.pathname.substr(0, location.pathname.lastIndexOf("/"))+"/";

      pathName = pathName.slice(1);

      wsh.run("wscript.exe \"" + pathName + scriptName + ".vbs\"", 1, true);

      wsh = null;

    }

  }

  else {

    alert("Your Browser doesn't support ActiveXObject");

  }

 

}

 

In this case the VBScript file must be in the same directory as the JavaScript file.

 

On this way we do have still the possibility to use our good old VBScript a little bit longer.

And so it is possible to use VBScript in the context of UI5 development with IE 11.

 

Enjoy it.

 

Cheers

Stefan

Viewing all 68 articles
Browse latest View live


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