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']
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])