def get_bo_data(bo_id, table, disp_fld): 

        conn = Connection(ashost='vipo65.swift.com', client='070', sysnr='00', user='******', passwd='Init1234')
        opt = [ dict(TEXT="BO_ID = '{}'".format(bo_id))]

        # get the list of fields in this table
        result = conn.call('RFC_READ_TABLE', QUERY_TABLE=table, DELIMITER='|', NO_DATA='X')
        all_fields = result['FIELDS']
        #print ('Number of fields in this table is: %d' % all_fields.__len__())

        localdata = []
       
        # we need to get get all fields 
        while all_fields.__len__() <> 0 :
        
            # prepare a list of fields to be retrieved that is max 512 bytes total 
            buff = 0
            fld = []
            while all_fields.__len__() <> 0 and buff + int (all_fields[0]['LENGTH']) < 512:
            #   print 'adding field {} that is {} byte(s)'.format( all_fields[0]['FIELDNAME'] , all_fields[0]['LENGTH']) 
                fld.append(dict(FIELDNAME=all_fields[0]['FIELDNAME']))
                buff = buff + int (all_fields[0]['LENGTH']) + 1  # add one for the size of the delimiter
                all_fields.pop(0)
            # query SAP for that subset
            result = conn.call('RFC_READ_TABLE', QUERY_TABLE=table, DELIMITER='|', OPTIONS=opt, FIELDS=fld)
                
            # store the results in our structure 
            # we need to loop on all rows retuned
            for i in  range (0, result['DATA'].__len__()) :
                if i >= localdata.__len__():
                    # append a row if we receive more data than we can store
                    localdata.append({})
                #print 'processing row {}'.format(i) 
                
                splitdata = result['DATA'][i]['WA'].split('|')
                #print 'we have extracted {} fields and received after parsing {} fields... no hard validation but it better should match ;-)'.format(fld.__len__(), splitdata.__len__())
                # populate our local storage
                for j in range(0, fld.__len__()):
                    mkey = fld[j]['FIELDNAME']
                    mval = splitdata.pop(0)
                    localdata[i][mkey]= mval
        conn.close()

        if result['DATA'].__len__() >= 1:
            print '    #{}'.format(table)
        for fd in disp_fld:
            line = '    BO Expect Info    {}    {}'.format(table, fd)
            toadd = ''
            toprint = 0
            for i in  range (0, result['DATA'].__len__()) :
                toadd = localdata[i][fd].lstrip("0").strip(" ")
                if toadd == '':
                    toadd = '${EMPTY}'
                else :
                    toprint = 1
                line = line + '    {}'.format(toadd)
            if (toadd != '')&(toprint == 1) :
                print line   
def Get_BOID (ereference):
        conn = Connection(ashost='vipo65.swift.com', client='070', sysnr='00', user='******', passwd='Init1234')
        opt= [ dict( TEXT="E_REFERENCE = '{}'".format(ereference)) ]
        result=conn.call('RFC_READ_TABLE', QUERY_TABLE='ZBOHEAD', OPTIONS=opt , FIELDS=[ dict( FIELDNAME='BO_ID')])
        conn.close()
        return result['DATA'][0]['WA']
Example #3
0
class SAPLibrary:

    def __init__(self, host='vipo65.swift.com', client='070', sysnr='00', user='******', passwd='Init1234'):
        self.conn = Connection(ashost=host, client=client, sysnr=sysnr, user=user, passwd=passwd)
        self.All_Tables={}
    
    def Get_BOID (self, ereference):
        opt= [ dict( TEXT="E_REFERENCE = '{}'".format(ereference)) ]
        result=self.conn.call('RFC_READ_TABLE', QUERY_TABLE='ZBOHEAD', OPTIONS=opt , FIELDS=[ dict( FIELDNAME='BO_ID')])
        self.conn.close()
        return result['DATA'][0]['WA']

    def Get_Sold_To (self, ereference):
        opt= [ dict( TEXT="E_REFERENCE = '{}'".format(ereference)) ]
        result=self.conn.call('RFC_READ_TABLE', QUERY_TABLE='ZBOHEAD', OPTIONS=opt , FIELDS=[ dict( FIELDNAME='SOLD_TO')])
        self.conn.close()
        return result['DATA'][0]['WA']

    def HSM_Item_Update_Status (self, vbeln, posnr):
        T_ITEMS= [ dict( VBELN = vbeln, POSNR = posnr, STATUS_NEW = "" ) ]
        result=self.conn.call('Z_HSM_ITEMS_UPD_STATUS', T_ITEMS = T_ITEMS, F_LOCK = 'X')
        self.conn.close()
        return result['T_RETURN']
    
    def Stop_BO(self,BOID,KUNNR):
        result = self.conn.call('Z_RFC_BO_ACCESS_01', IN_BO=dict(BO_ID='{}'.format(BOID)), READATTACH='', REQUEST='READ', REQUESTOR='{}'.format(KUNNR))
        BO=deepcopy(result['OUT_BO'])
        print pp.pformat(BO)        
        # this field is used by some formz, cannot update it anymore
        #BO['FREE_TEXT'] = 'Stopped by a Robot'
        BO['STATUS'] = 'Stopped'
        self._delete_None_elments(BO)
        # print BO        
        result = self.conn.call('Z_RFC_BO_ACCESS_01', IN_BO=BO, READATTACH='', REQUEST='CHANGE', REQUESTOR='{}'.format(KUNNR), NO_COMMIT='' )
        #print '*INFO* {} '.fomat(result['OUT_BO'])
        self.conn.close()
        return result['OUT_BO']['STATUS']
    
    def BO_Expect_Info(self,Table,Field,*Values):
        #create new entry for this BO table; it is a list that will contain the different rows to validate
        if not Table in self.All_Tables:
            self.All_Tables[Table]=[]
        # now store all values
        for i in range(0,Values.__len__()):
            if i >= self.All_Tables[Table].__len__():
                self.All_Tables[Table].append({})
#            self.All_Tables[Table][i].append({ Field : Values[i]})
            self.All_Tables[Table][i][Field]=Values[i]
        self.conn.close()
        return self.All_Tables
            
    def get_bo_data(self,bo_id, table): 
        
        opt = [ dict(TEXT="BO_ID = '{}'".format(bo_id))]
        # get the list of fields in this table
        result = self.conn.call('RFC_READ_TABLE', QUERY_TABLE=table, DELIMITER='|', NO_DATA='X')
        all_fields = result['FIELDS']
        #print ('Number of fields in this table is: %d' % all_fields.__len__())
        
        localdata = []
        
        # we need to get get all fields 
        while all_fields.__len__() <> 0 :
        
            # prepare a list of fields to be retrieved that is max 512 bytes total 
            buff = 0
            fld = []
            while all_fields.__len__() <> 0 and buff + int (all_fields[0]['LENGTH']) < 512:
            #   print 'adding field {} that is {} byte(s)'.format( all_fields[0]['FIELDNAME'] , all_fields[0]['LENGTH']) 
                fld.append(dict(FIELDNAME=all_fields[0]['FIELDNAME']))
                buff = buff + int (all_fields[0]['LENGTH']) + 1  # add one for the size of the delimiter
                all_fields.pop(0)
            # query SAP for that subset
            result = self.conn.call('RFC_READ_TABLE', QUERY_TABLE=table, DELIMITER='|', OPTIONS=opt, FIELDS=fld)
            
            # store the results in our structure 
            # we need to loop on all rows retuned
            for i in  range (0, result['DATA'].__len__()) :
                if i >= localdata.__len__():
                    # append a row if we receive more data than we can store
                    localdata.append({})
                #print 'processing row {}'.format(i) 
                
                splitdata = result['DATA'][i]['WA'].split('|')
                #print 'we have extracted {} fields and received after parsing {} fields... no hard validation but it better should match ;-)'.format(fld.__len__(), splitdata.__len__())
                # populate our local storage
                for j in range(0, fld.__len__()):
                    mkey = fld[j]['FIELDNAME']
                    mval = splitdata.pop(0)
                    localdata[i][mkey]= mval
        self.conn.close()
        return localdata        
        
    def Validate_BO(self,BOID):
        GlobErrors=[]

        outputdir=BuiltIn().replace_variables('${OUTPUTDIR}')
#        outputdir='c:/no_backup'
        
        for table in self.All_Tables:
            tErrors=0
            path=os.path.join(outputdir,'{}_{}.txt'.format(BOID,table)) 
            f=open(path,'w')
            print '*INFO* Validating Table {} for BO {}'.format(table,BOID)
            saprows=self.get_bo_data(BOID, table)
            #lets print the rows we will compare for information
            f.write('*INFO* For BO {} and table {}, SAP contains:\n\n'.format(BOID,table))
            for i in range (0,saprows.__len__() ):
                f.write( 'row {} -- {}\n'.format(i, saprows[i]))

            f.write('\n*INFO* For BO {} and table {}, SAP contains (selected fields from the above):\n\n'.format(BOID,table))
            # get the keys that are both in SAP and the test
            for k in self.All_Tables[table][0].iterkeys(): # loop on all our expected keys
                if saprows.__len__() > 0:
                    if k in saprows[0]:                     # if it exists in SAP, we will print all the values there
                        f.write( 'BO Expect Info\t{}\t{}'.format(table, k))
                        for s in range (0,saprows.__len__() ):
                            f.write( '\t{}'.format(self._parseString(saprows[s][k])))
                        f.write( '\n')
                    else:
                        f.write( 'BO Expect Info\t{}\t{}\t{}\n'.format(table, k, 'ERROR: FieldName not found in SAP!'))
       
            f.write('\n*INFO* For BO {} and table {}, the test expects:\n\n'.format(BOID,table))
            for i in range (0,self.All_Tables[table].__len__() ):
                f.write(  'row {} -- {}\n'.format(i,self.All_Tables[table][i]))
            # first check if we have the same number of lines between SAP and the TEST, if not, there is a problem
            if saprows.__len__() == self.All_Tables[table].__len__():
                # and now the real compare, going through all the test data (loop on t)
                for t in range (0,self.All_Tables[table].__len__() ):
                    # check that we do not expect fields that would be missing in SAP (often means a typo in the test)
                    unexisting= {k:v for k,v in self.All_Tables[table][t].iteritems()
                                 if not  (k in saprows[0] )   }    
                    if unexisting:
                        print 'ERROR: from Test data row {}, following fields are missing in SAP: {}'.format(t,unexisting)
                        GlobErrors.append('{} - unkown in SAP: {}\n'.format(table,unexisting))
                        tErrors+=1
                    else:
                        # we can check if we have a matching line in the SAP Table (loop on s)
                        # while looping we will keep the id of the matching line and the running best match
                        Nr_of_full_Match=0
                        Best_Match_Row=None
                        Best_Match_Limitations={}
                        
                        for s in range (0,saprows.__len__() ):
#                            notmatch= dict((k, (v, saprows[s][v]) for k, v in self.All_Tables[table][t].iteritems() 
#                                  if saprows[s][k] != v) 
                            notmatch = {k:v for k,v in self.All_Tables[table][t].iteritems()
                                      #if parseStr( saprows[s][k]) != parseStr(v) }
                                      if self._parseString( saprows[s][k]) != self._parseString(v) }
                            if notmatch:
                                #store the best match if none availlable or this row is better and notmatch should not be the full list of fields
                                if notmatch.__len__() < self.All_Tables[table][t] and Best_Match_Row == None or Best_Match_Limitations.__len__() > notmatch.__len__():
                                    Best_Match_Limitations=notmatch
                                    Best_Match_Row=s
                            else:
                                Best_Match_Row=s
                                Nr_of_full_Match=Nr_of_full_Match+1
                                
                        if Nr_of_full_Match == 1:
                            print 'row {}: OK,  full match found against SAP row {}'.format(t,Best_Match_Row) 
                        elif  Nr_of_full_Match > 1:
                            print 'row {}: ERROR, mutliple full match found'.format(t)
                            GlobErrors.append('{} - Mutltiple matches found in SAP\n'.format(table))
                            tErrors+=1
                        else:
                            print 'row {}: ERROR,No full match found, the closest match was vs line {} with following fields not matching: {}'.format(t,Best_Match_Row,Best_Match_Limitations) 
                            GlobErrors.append('{} - Could not match: {}\n'.format(table,Best_Match_Limitations))
                            tErrors+=1
            else:
                print 'ERROR expected {} row(s), found {} row(s)'.format(self.All_Tables[table].__len__(),saprows.__len__() )
                GlobErrors.append('{} - expected {} row(s), found {} row(s)\n'.format(table,self.All_Tables[table].__len__(),saprows.__len__()))
                tErrors+=1
            f.close()
            if tErrors >0:               
                print '*HTML* <font color="red">ERROR for BOID {} in table {} </font>; look <a href="{}_{}.txt">here</a> for the details'.format(BOID,table,BOID,table)
            else:
                print '*HTML* <font color="green">SUCCESS for BOID {} in table {} </font>; look <a href="{}_{}.txt">here</a> for the details'.format(BOID,table,BOID,table)
                
        # test the fail keyword
        if GlobErrors.__len__() > 0:
            errstr = '*HTML* <font color="red">BO {} validation failed:</font><br>'.format(BOID)
            errstr+='<ul>'
            for t in GlobErrors:
                errstr+='<li>{}</li>'.format(t)
            errstr+='</ul>'
            raise AssertionError(errstr)
            
    def _parseString(self,x):
        x=str(x).strip()
        if re.match('^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$', x, flags=0):
        # IP addresses are stored in SAP as a big number => transforming this one
            IP = re.split('\.', x, flags=0)
            x = (int(IP[0])<<24) + (int(IP[1])<<16) + (int(IP[2])<<8) + (int(IP[3])) 
        elif x.isalpha():
            x= str(x).strip()
        elif x.isdigit() :
            x= int(x)  
        elif x.isalnum() :
            x= str(x).strip()  
        elif len(set(string.punctuation).intersection(x)) == 1 and x.count('.') == 1:
            x=  float(x) or str(x).strip() 
        return x
            
    
    def _delete_None_elments(self, nodes):
        # recursively traverse any list or dict and delete any None element (causing RFC exceptions)
        if type(nodes) is list:
            for n in nodes:
                self._delete_None_elments(n)
        elif type(nodes) is dict:
            for k in nodes.keys():
                if nodes[k] is None:
                    del nodes[k]
                elif type(nodes[k]) is dict:
                    self._delete_None_elments(nodes[k])
                elif type(nodes[k]) is list:   
                    self._delete_None_elments(nodes[k])