2012-06-26
Email SAP SQVI reports.
2012-06-21
Telephone Sale
“You are looking for an IS/IT manager?” I have asked this ominous question a zillion times and the unsuspecting answer is always ‘yes?’. Then I finish the conversation with ” I’m sorry but he is in our IT-service company IT Help, I do not know his name but please call our switchboard and ask for the manager of IT help . ThankyouandGoodbye.”
These days I am no longer responsible for IT and I can just hand salesmen over to others.
When I was IT manager I got lots of calls from telephone salesmen. On a bad day it could be more than 15 calls on a good day about five calls. To survive you got to find a strategy to limit both the number of calls and the time of each call. These salesmen are doing an honest (and very hard) work and deserves to be treated with respect. Nevertheless you have to cut him/her short to be able to do your work. These calls are especially annoying when you are in the middle of a ‘debugging session’ or some kind of thought process that demands thinking in many layers or structures, since it takes you time to go to the bottom. Being disturbed in the middle may force you to start all over again. Why didn’t I just turned off the telephone? I wanted to be a modern open ‘always online manager’ so I very seldom switched off my telephone.
When these calls started to become a nuisance, I developed a web-app for these salesmen where they could leave a message and I could called them up if I was interested. Then I talked with our switchboard staff asking them to instruct the sales guys to go to the web-app. But the switchboard guys could not do this, that would be all too time consuming. Then I asked for a special IT-manager sales telephone-number with an automatic reply instructing callers how to use my sales web-app. But that was against company policy! I then decided I should end these calls in less than two minutes. This is in fact harder than it sounds, a telephone salesman is a specialist of keeping a conversation going. It is very much a question of taking the initiative before the opponent have a chance to start his sales pitch. You must tell the opponent what to do. Interrupt the guy when he has introduced himself, and tell him what he has to offer is not relevant for you and tell him to call the next one on his list. (You know what he/she want to offer after the first introduction.) Remember to be polite, (it can be hard when the 15th guy call on a busy day). It can also be someone searching for a job, they can be hard to spot after the first introduction they are also in a selling position. These guys not only deserves respect they also deserves some of your precious time.
Why does these salesmen deserves your respect in the first place? At least in my country, Sweden, we have traditionally a mild contempt for sales in general. And I suffered from the same mild contempt for sales guys until I worked with pre- and post-sales support, I then realized how hard and frustrating sales jobs actually are. Without a good sales-force a company is not worth a cent. Without the sales guys, you and I would not have a job. Actually everyone deserves respect. Well almost, there are some real ...
I wrote this post after yet another of these calls and I feel like a grumpy old man.
2012-06-17
PHP job scheduler examples
This is a list of examples of background jobs built with my PHP job scheduler .
Most of these jobs are ETL processes where I extract data from SAP.
- Extracting data from SAP
- Multi-thread a job for speed
- Business Intelligence real time delta load
- Extract SAP forecasts with RFC_READ_TABLE
- Manage SAP background processes
- Make MySql information_schema useful
- Templates and alternative iterators
- Extract Material Reservations from SAP with RFC_READ_TABLES
- Extract the shop calendar from SAP
- Performant extraction of stock figures from SAP
- Zip and send a file to a Windows share
- From MySQL to Twitter
2012-06-16
Load SAP forecasts into MySQL with RFC_READ_TABLE and PHP
Update: If this post is of interest you should also read this post.
Summary
Forecast
- Look for the product (WERKS & MATNR) in MAPR.
- Take all PNUM1 in MAPR and find all corresponding rows in table PROP.
- Take all PNUM2 in PROP and find all corresponding rows in PROW.
Ok let’s fetch the data from SAP and import it into our Data Warehouse.
2012-06-11
Outsourcing people
I didn’t participate myself in my first encounter with outsourcing. I left the company 2 years before it actually happened. But I had lots of friends still in the company and I assisted in the handover process. It was ‘my’ old company Atlas Copco Data AB that was outsourced to the company Ericson. AC Data at that time 1987 managed the computer operations of Atlas Copco. The reason for outsourcing the operations were mainly size not efficiency, Atlas Copco’s operations were streamlined and slimmed down to a minimum, the operation were automated, the personal staff highly professional.
This was one of the first large outsourcing deal in Sweden and there were a shortage of skilled IBM mainframe technicians. AC Data could negotiate from a strong position, the CEO actually let the employees participate in negotiations and one of the conditions were all interested should have a new job at the outsourcing company (Ericson) equal or better than he/she had at AC Data. During this time I happened to meet the CEO and he said ‘I never seen people so motivated to lay themselves off’. Very few were happy about the transition to Ericson, but that was more a fact that they all knew each other since many years and now they were split up in a new huge organization. This was probably as happy an outsourcing can be for the employees.
The next encounter with outsourcing hit me more direct I was working for a big computer service bureau; Esselte Datacenter. The owner negotiated the conditions without any of us employees were involved at all. My (still) good friend the CEO resigned and in came a consultant CEO. He was probably a good guy, but his mission was clear. ‘Do the transition to as fast as possible, only those the outsourcing company wants will be offered a job’. Of about 120 employees we were a handful that got offers in the end and we all thankfully accepted , 1992 Sweden had hit a severe recession and jobs were not to be found. Yes I was very thankful for the offer. At the time I was CTO and become very involved in the handover. I knew that I was important for the continuous operation. In the middle of the transfer I handed in my resignation. This was a calculated risk I had no other job, but as I expected I was offered a nice bonus if I promised to stay a year, later I understood I could easily have doubled that bonus, but anyway it was a nice bonus. (I demanded the same bonus for a colleague, which to my surprise was granted.)
The strategy for this outsourcing was very different from AC Data. The new CEO promised openness, we were all going to get all information about all aspects of the outsourcing deal, but in reality we only got information on ‘as needed base’ and as late as possible. I had the impression we were deliberately stressed so we should not have the time to think but just do our work. Vague talk about job offers but no promises from the consultant CEO. I had one guy that was desperate, he had just bought a house with a hefty mortgage, three small kids and an unemployed wife. This guy I actually could help. I ‘promoted’ him to part time assembler programmer and made him responsible for a credit information system and persuaded the outsourcing company he was vital for the system, they bought it and he was given a job. To my grief I could not help more than one, there were many good guys there and there were a lot of tragedies. One guy told me he was considering take up smuggling (stolen?) cars between Spain and Morocco but that is another story. This outsourcing was not happy for anyone of us employees, not even those of us who had a new job to go to, I stayed just over a year at the outsourcing company. From this outsourcing I learned leave do not stay one minute more than needed when you are at the risk being outsourced. Even if you can gain some dough by staying it is not worth it.
Some years ago as an IT-manager of the company I’m working for, I had to outsource/lay off some of my staff, due to an anticipated financial crisis. They were not many but that is no consolation for those afflicted. And it is not a pleasant task for the manager to lay off anyone, and these were good guys. I used my experience from the previous encounters with outsourcing.
The first thing I did, on a Monday morning ( always tell bad news beginning of the week, NEVER EVER on a Friday ), was to tell the afflicted guys as straight as I could what was going to happen. I told them they were going to get laid off, this was inevitable, the chance they had was to impress the outsourcing company, since we not in a position to demand the outsourcing company to employ them. The reaction was not what I had anticipated. They just did not believe me! They thought they were more or less irreplaceable. This was of course a mistake from their side, no employee is irreplaceable. Do not ever for a second believe you as an employee is irreplaceable, you are replaceable and expendable .
Next thing I did was to select an outsourcing company. I made a list of the candidate outsourcing companies the best at the top of the list. I draw a line between those I thought I could work with and those (below the line) I didn’t thought I could work with and told my boss to pick a company above the line. He chose the second from the top, and we are still doing business with them.
My staff found jobs outside the company. They did what I should have done myself, but I tried to arrange for them to go to the outsourcer, but they were not interested.
Outsourcing and laying people off are sometimes necessary, it is your task as manager to do this as smooth and painless as possible for all. This is probably the most stressful task you will encounter as a manager, and you should not do this alone, you should not only engage the HR department but the whole management team if possible.
Never lie, never give false hope and never promise anything you cannot hold. Be frank, open and tell your staff as early as possible what is going to happen. These are the best advice I can give you, I pray you never have to use them.
2012-06-10
Using XBP to manage SAP jobs from PHP
SAP XBP vs 3, is the API for accessing SAP background processes. XBP requires you to log on to the XMI-XBP subsystem. XMI= External Monitor Interface , XBP= External Interface for Background Processing . This sounds awful complicated but It is not that complex. You logon to the XBP subsystem by calling 'BAPI_XMI_LOGON' this is a normal BAPI call and then you just proceed with the XBP BAPI functions you like to use.
Before you proceed you should learn about XMI and XBP .
Calling the XMI and XBP BAPIs from PHP is no different from calling any other BAPIs or RFC functions from PHP. First you need to install an interface between PHP and SAP. Google on SAPRFC by Eduard Koucky, this project looks dead to me, but Axel Bangert seems to have picked up SAPRFC. Piers Harding have created a Unicode aware interface SAPNWRFC. You should Google around and pick the right interface for you. (I’m slowly migrating from SAPRFC to SAPNWRFC. Examples in this post are using Koucky’s original SAPRFC, which is the simpler interface to use.)
I have written a job scheduler in PHP , there I have created some functions of my own on top of the SAPRFC and SAPNWRFC interfaces, for logging on to SAP and calling SAP BAPI and RFC functions. The examples here are using my scheduler and my interface functions. Before you proceed you should enjoy my post extract data from SAP , here you can learn a very useful technique how to use SAP’s transaction SE37 to set up call to SAP functions and transform the in- and out-put parameters to my job scheduler in PHP.
I use the SAP XBP interface to monitor SAP batch jobs or background processes, i.e. I check if SAP jobs have run (successfully) before I kick off my external PHP jobs. (XBP is not limited to monitor you can fully manage background processes in SAP via XBP.) Here is another example .
This somewhat artifical example loads all SAP jobs with names starting with ‘TOO’ into MySQL, if the job ‘TRPX_LE_WRHSEMONITOR_ALL’ was run successfully the during the last 2 days. Then an Excel sheet is created and mailed.
The important pieces of code here are:
the prereq, which checks if job(s) are run in SAP.
<xmi><session>XBP</session></xmi>, explicit log on to XMI/XBP
<schedule name='BatchList' logmsg='Automagically load of SAP rfc output into mysql table(s)'>
<tag><name>DW_DB</name><value>test</value></tag> <!-- MySql database -->
<!-- only run if SAP job executed ok the last 2day --> <prereq type='sap' jobname='TRPX_LE_WRHSEMONITOR_ALL' username='*' executed='now-2d'/>
<job name='get_Jobs' type='script' data='sap2.php'> <sap> <xmi><session>XBP</session></xmi> <rfc> <name>BAPI_XBP_JOB_SELECT</name> <import> ('EXTERNAL_USER_NAME',$sapContact['xmi']['EXTERNAL_USER_NAME']) ,('SELECTION','AL') ,('JOB_SELECT_PARAM',array('JOBNAME'=>'TOO*', 'USERNAME'=>'*','ABORTED'=>'X','FINISHED'=>'X','ACTIVE'=>'X', 'FROM_DATE'=>@TODAY)) </import> </rfc> </sap> <script><sql> <autoload>replace</autoload><database>@DW_DB</database><truncate>yes</truncate> <table>JOB_HEAD=tooJobHeads</table><tableX>SELECTED_JOBS</tableX> </sql></script> </job>
<job name='generate_report' type='sql'> <sql> USE @DW_DB; SELECT * from tooJobHeads; </sql> <sqlconverter> <name>sqlconverter_ExcelBasic.php</name> <target>report</target> </sqlconverter> </job>
<job name='send_mail' type='sendmail'> <mailer>phpmailer</mailer> <recipients>lasse@mysite.se</recipients> <subject>Fyi: batch jobs and XMI</subject> <div> Hi, This is an artificial example but it shows the Sap connectivity implemented in ACTADW.
The mail you recieved contains two attachments this file (schedule.xml) and an SAP job status report. The schedule.xml file contains all commands necessary: 1 Check that the Sap job TRPX_LE_WRHSEMONITOR_ALL is sucessfully executed during the last 2 days, otherwise the execution is intercepted. 2 Extract the information from SAP. 3 Load the extract into ACTADW. 4 Create a report. 5 Create this mail and send it. Lars Johansson </div> <attachment> <name>batchStatus.XLS</name><file>@J_generate_report/report0.XLS</file> </attachment> <attachment> <name>schedule.xml</name><file>/dw/schedule/example10.xml</file> </attachment> </job> </schedule> |
The PHP code for Logon to XMI
/** * This function logs on to the Sap XMI subsystem * * Before you log on to the SAP XMI subsystem, you should log on to SAP with {@link connect2sap()}, and before you * disconnect from SAP you should log off from XMI with {@link sapXmiLogoff()}. * * Syntax: <samp><xmi session='XAL'/></samp> * @todo update SAP3.php * @see sapXmiLogoff() * @param array $context * @param object $sap Handle to saprfc interface * @return string $sessionid The XMI sessionid * @uses callSapFunction() */ function sapXmiLogon($context,&$sap,$xmiType='XBP'){ $log = $GLOBALS['log']; $log->enter('Info'); if (!$sap) return FALSE; $sapContact = $context['sapinfo']; switch ($xmiType) { case('XBP'): $fce=callSapFunction($sap,'BAPI_XMI_LOGON',array( array('import','EXTCOMPANY',$sapContact['xmi']['EXTCOMPANY']) ,array('import','EXTPRODUCT',$sapContact['xmi']['EXTPRODUCT']) ,array('import','INTERFACE','XBP') ,array('import','VERSION','3.0') )); break; default: $log->logit('Error',"Unknown SAP XMI session type=$xmiType"); return FALSE; } if ($fce->GetStatus() == SAPRFC_OK) { $log->logit('Info',sprintf("SAP XMI session established sessionid=%s",$fce->SESSIONID)); } else { return FALSE; } $sessionid = $fce->SESSIONID; $fce->close(); return $sessionid; }
|
And the code to evaluate the SAP prereq at the beginning of the the XML schedule example ‘BatchList’ above.
/** * This function evaluates a Sap job predecessor by calling BAPI_XBP_JOB_SELECT * * The parm 'SINCE' allows a more relaxed call to BAPI_XBP_JOB_SELECT. * <samp> * <stmt type='sap' JOBNAME='TRPX_LE_WRHSEMONITOR_ALL' USERNAME='*' SINCE='now-1d'/> * </samp> * Parm 'SINCE' calls {@link tagday.php} to convert the argument to a point in time, which is translated into * the parameters 'FROM_DATE' and 'FROM_TIME'. The FINISHED parameter will also be set ('X') as default. * * Parms to BAPI_XBP_JOB_SELECT: <br> * 1 JOBNAME Background job name * 2 USERNAME Initiator of job/step scheduling * 3 FROM_DATE Planned Start Date for Background Job * 4 FROM_TIME Planned start time for background Job * 5 TO_DATE Planned Start Date for Background Job * 6 TO_TIME Planned start time for background Job * 7 NO_DATE Selection flag for jobs without start date * 8 WITH_PRED Selection flag for jobs with start after predecessor * 9 EVENTID Background Processing Event * 10 EVENTPARM Background Event Parameters (Such as, Jobname/Jobcount) * 11 PRELIM State of Background Job * 12 SCHEDUL State of Background Job * 13 READY State of Background Job * 14 RUNNING State of Background Job * 15 FINISHED State of Background Job * 16 ABORTED State of Background Job * * @param array $context * @param array $cb this is $schedule for schedule prereqs and $job for job prereqs * @param array $stmt the schedule prerequisit statement * @param object $sap Handle to saprfc interface * @return int $jobcount no of executed predecessor jobs with status finished|complete, caller can test for TRUE * @uses callSapFunction() * @uses tagday.php */ function checkSapPredecessor($context,$cb,$stmt,&$sap){ $log = $GLOBALS['log']; $log->enter('Info'); if (!$sap) return FALSE; if(!array_key_exists('JOBNAME',$stmt)) return TRUE; // We only check if sap is alive, and if we pass here sap is responding so...
$sapContact = $context['sapinfo']; $jsp = array_intersect_key(array_change_key_case($stmt, CASE_UPPER) ,array('JOBNAME' => '','USERNAME' => '','FROM_DATE' => '','FROM_TIME' => '','TO_DATE' => '' ,'TO_TIME' => '','NO_DATE' => '','WITH_PRED' => '','EVENTID' => '','EVENTPARM' => '' ,'PRELIM' => '','SCHEDUL' => '','READY' => '','RUNNING' => '','FINISHED' => '','ABORTED' => '' ));
if(array_key_exists('SINCE',$stmt) or array_key_exists('since',$stmt)){ //relaxed form $script = 'tagday.php'; $proc = $context['opsproclib']; $parm = 'YmdHis'.$stmt['SINCE']; $log->logit('Info',"Calling $proc$script with parm=$parm"); /** * Require tagday.php to evaluate the executed statement */ $dtStr = require("$proc$script"); $fromDate = substr($dtStr,0,8); $fromTime = substr($dtStr,8,6); if(!array_key_exists('FROM_DATE',$jsp)) $jsp['FROM_DATE'] = $fromDate; if(!array_key_exists('FROM_TIME',$jsp)) $jsp['FROM_TIME'] = $fromTime; if(!array_key_exists('FINISHED',$jsp)) $jsp['FINISHED'] = 'X'; if(!array_key_exists('USERNAME',$jsp)) $jsp['USERNAME'] = '*'; } $log->logit('Info',vsprintf("Enter checkSap %s, %s, %s, %s, %s", $jsp));
$log->mode('None'); //We do not wish to see SAP empty selections warning messages $fce=callSapFunction($sap,'BAPI_XBP_JOB_SELECT',array( array('import','EXTERNAL_USER_NAME',$sapContact['xmi']['EXTERNAL_USER_NAME']) ,array('import','SELECTION','AL') ,array('import','JOB_SELECT_PARAM',$jsp) )); $log->reset('mode'); // Reset log to original verbose mode
if ($fce->GetStatus() == SAPRFC_OK || $fce->GetStatus() == '99') { $jobcount = 0; $fce->JOB_HEAD->Reset(); while ( $fce->JOB_HEAD->Next() ) { $jobcount++; } } else { return FALSE; } if(!$jobcount) $log->logit('Note',sprintf("SAP predecessor job %s not executed successfully after $fromDate $fromTime",$stmt['jobname'])); return $jobcount; } |