How to access SAP using Excel VBA? - excel

I am trying to log on to SAP. The Excel VBA code gives me a popup window confirming my information however when I submit the form it does not take me to a new SAP window.
Additionally is there a way to automate all the popup boxes asking for confirmation on my information? I want this code eventually to run at certain times of the day, and I might not be available to input any data.
Sub login1()
Dim sap As Object
Dim conn As Object
Set sap = CreateObject("SAP.Functions")
Set conn = sap.Connection
conn.System = "System Test Environment"
conn.client = "100"
conn.user = "user"
conn.Password = "password"
conn.Language = "EN"
If conn.logon(0, False) <> True Then
MsgBox "Logon to the SAP system is not possible", vbOKOnly, "Comment"
Else
End If
End Sub

This Macro will never open a SAP Window - it will create an SAP-Object within VBA where you can work with SAP-RFC-Functions. (Reading Data from SAP, Writing Data into SAP)
In your version the SAP connection will be unaccessible after "End Sub". You have to declair the Object outside the sub.
This works silent (without dialog) for me:
Dim sap As Object
Public Function login1() As Boolean
Set sap = CreateObject("SAP.Functions")
sap.Connection.System = "System Test Environment"
sap.Connection.client = "100"
sap.Connection.user = "user"
sap.Connection.Password = "password"
sap.Connection.Language = "EN"
If sap.Connection.logon(0, False) <> True Then
sap.RemoveAll
MsgBox "Logon to the SAP system is not possible", vbOKOnly, "Comment"
Else
login1 = true
End If
End Function
Public Function SAPLogoff()
On Error Resume Next
sap.RemoveAll
sap.Connection.logoff
LoggedOn = False
Set sap = Nothing
'Set conn = Nothing
End Function

Since you want to "open a new SAP Window" you have to make a different approach!
At First try to open the new instance of SAP from the DOS Commandline with "sapshcut":
C:\Program Files (x86)\SAP\FrontEnd\SAPgui\sapshcut.exe -system="System Test Environment" -client="100" -user="user" -password="password" -language="EN"
If your SystemName has no Spaces (and I belive so!) then you can write it also like:
C:\Program Files (x86)\SAP\FrontEnd\SAPgui\sapshcut.exe -system=SystemTestEnvironment -client=100 -user=user -password=password -language=EN
When this call works with your credentials, then you can transfer it to Excel like this:
Sub login1()
Call Shell("C:\Program Files (x86)\SAP\FrontEnd\SAPgui\sapshcut.exe -system=SystemTestEnvironment -client=100 -user=user -password=password -language=EN",vbNormalFocus)
End Sub
You could also add a transaction by adding "-command=your_tcode" to the String.
If your have spaces in the parameters and you could only start it with -system="System Test Environment" from the Commanline, you will have to escape this in Excel with -system="""System Test Environment""" (!)

Related

Macro won't run in my latest system(capturing data)

I have 2 macro version code that runs in old version of PCOMM System(capturing data). However After the update of latest PCOMM System(Capturing data). My macro will run as "Macro run complete" but the data didn't reflect in excel.
Here's my Version 1 My code can't capture data in my new latest PCOMM system(capturing data site) However macro says "Macro Run Complete!" to get the data. Show images and sample code below
Public Function Extra_Init() As Boolean On Error
GoTo ExitExtraInit
Extra_Init = False
Set goExtraSys = CreateObject("PCOMM.autECLConnList")
goExtraSys.Refresh
Set goSessions = CreateObject("PCOMM.autECLOIA")
goSessions.SetConnectionByHandle (goExtraSys(1).Handle)
giSessionCount = goExtraSys.Count
If giSessionCount = 1 Then
Set goSession = CreateObject("PCOMM.autECLPS")
goSession.SetConnectionByHandle (goExtraSys(1).Handle)
If goSession.CommStarted Then
Extra_Init = True
Else
Extra_Init = False
End If
End If
Exit Function
Here's my Version 2 My Microsoft Visual Basic when being run, an error prompts showing "class not registered"
Sample images below.
Error appears in this part of code.
Set Exl = GetObject(, "Excel.Application")
Set Extra = CreateObject("PCOMM.autECLConnList")
Extra.Refresh
Thank you in advance!
Get the data in my latest version of PCOMM System

Find SAP GUI session opened for given SAP system

First I want to check via VBA, before I do some transactions in SAP GUI, if a connection is already open. I am not able to login a second time, so I need to stay in the same connection.
Secondly I want to open another session. The second problem has been solved, if I assume SAP GUI is already open. But I don't know it for sure. So I need to find a way to access the current SAPGUI and Application and Connection, if they exist. If not, the standard code of If Not IsObject(SAPGUI) Then… is fine. But how do I define these variables correctly to check, if they are „filled“ Objects or not?
Thanks for help!
Based on a script by S. Schnell you can use the follwing function to find a free session
Function findGuiSession(ByVal sapSID As String, Optional tCode As String) As SAPFEWSELib.GuiSession
' this will find a free session using the systemnam resp. SID
' and optional one can also supply a transaction to
Dim CollCon As SAPFEWSELib.GuiComponentCollection
Dim CollSes As SAPFEWSELib.GuiComponentCollection
Dim guiCon As SAPFEWSELib.GuiConnection
Dim guiSes As SAPFEWSELib.GuiSession
Dim guiSesInfo As SAPFEWSELib.GuiSessionInfo
Dim i As Long, j As Long
Dim SID As String, transaction As String
'On Error GoTo EH
Dim guiApplication As SAPFEWSELib.guiApplication
Set guiApplication = getGuiApplication
If guiApplication Is Nothing Then
Exit Function
End If
Set CollCon = guiApplication.Connections
If Not IsObject(CollCon) Then
Exit Function
End If
' Loop through all existing connections
For i = 0 To CollCon.Count() - 1
Set guiCon = guiApplication.Children(CLng(i))
If Not IsObject(guiCon) Then
Exit Function
End If
Set CollSes = guiCon.Sessions
If Not IsObject(CollSes) Then
Exit Function
End If
' Now loop through all existing sessions
For j = 0 To CollSes.Count() - 1
Set guiSes = guiCon.Children(CLng(j))
If Not IsObject(guiSes) Then
Exit Function
End If
If guiSes.Busy = vbFalse Then
Set guiSesInfo = guiSes.Info
If guiSesInfo.user = "" Or guiSesInfo.user = "SAPSYS" Then
' Logon Screen - cannot be used
Else
If IsObject(guiSesInfo) Then
SID = guiSesInfo.SystemName()
transaction = guiSesInfo.transaction()
' Take the first one - In case one could also use the transactionaction addtionally
If Len(tCode) = 0 Then
If SID = sapSID Then
Set findGuiSession = guiSes
'FindSession = True
Exit Function
End If
Else
If SID = sapSID And transaction = tCode Then
Set findGuiSession = guiSes
'FindSession = True
Exit Function
End If
End If
End If
End If
End If
Next
Next
Exit Function
'EH:
End Function
Function getGuiApplication() As SAPFEWSELib.guiApplication
On Error GoTo EH
Set getGuiApplication = GetObject("SAPGUI").GetScriptingEngine
EH:
End Function
For this code to run you need to add a reference to the SAP library, described here
The following piece of code uses the above function to connect to a system with the name P11, starts the transaction MB52 and downloads the result in a Excel file
Option Explicit
Sub getMB52_data()
Dim guiSes As SAPFEWSELib.GuiSession
Set guiSes = getGuiSession("P11")
If Not guiSes Is Nothing Then
With guiSes
.StartTransaction "MB52"
.FindById("wnd[0]/usr/ctxtMATNR-LOW").Text = "<MATNR_LOW<" ' replace with a material nr
.FindById("wnd[0]/usr/ctxtMATNR-HIGH").Text = "<MATNR_HIGH<" ' replace with a material nr
.FindById("wnd[0]/usr/ctxtWERKS-LOW").Text = "<WERKS>" ' replace wiht a plant
.FindById("wnd[0]/tbar[1]/btn[8]").Press
.FindById("wnd[0]/tbar[0]/okcd").Text = "&XXL"
.FindById("wnd[0]/tbar[0]/btn[0]").Press
.FindById("wnd[1]/tbar[0]/btn[0]").Press
.FindById("wnd[1]/usr/ctxtDY_PATH").Text = "<xlPath>" ' Pathname
.FindById("wnd[1]/usr/ctxtDY_FILENAME").Text = "<xlFile>" ' filename
.FindById("wnd[1]/tbar[0]/btn[11]").Press
End With
Else
MsgBox "No free SAP Session", vbOKOnly + vbInformation, "SAP Verbindung"
End If
End Sub
Function getGuiSession(sapSID As String, Optional tCode As String) As SAPFEWSELib.GuiSession
Dim guiApp As SAPFEWSELib.guiApplication
Set guiApp = getGuiApplication
If Not guiApp Is Nothing Then
Set getGuiSession = findGuiSession(sapSID, tCode)
End If
End Function
Additonal remarks: (hopefully answering some questions in the comments)
Gui Connection: A GuiConnection represents the connection between SAP GUI and an application server. Connections can be opened from SAP Logon or from GuiApplication’s openConnection and openConnectionByConnectionString
methods
So, in other words a gui connection is a kind of login to an SAP system. And usually you have more than one SAP system in your organization. If you follow the guidelines you have a DEV, QUAL and PROD for a given system environment. Each of this system is identifid by a SID
What is SID?
SID is a unique identification code for every R/3 installation (SAP system) consisting of a database server & several application servers. SID stands for SAP System Identification. SAPSID — a three-character code such as C11, PRD, E56, etc.)
An SID is unique within the organization. Usually SAP licence only allows a user to login to a productive system only once, i.e. you cannot use the same user on different computers and you cannot even login to a SAP system with the same user on the same computer twice.
Having said that: One might be tempted to use guiApplication.Children(0) instead of looping through all connections as done in findGuiSession. And this will work as long as you can make sure that you are only logged on to one SAP system and it is the right one. But in my experience this is often not the case.
The parameter SID in findGuiSession tells the function which system to look for. As written above SID is unique and therefore identfies the system you want to use.
Using tCode in findGuiSession is optional and just forces the user to have a session in place with a given tCode already started. I use this very seldom.
OpenConnection: If you open a connection with the function OpenConnection("<SYSTEM>") you can, of course, use the returned object in order to work with it. But this only does a logon to the system if you have a kind of single sign on in your organization in place. Otherwise you have to provide logon credentials. I do not use this because I do not want to take care of this. And it also can happen that a password change is requested during the logon and I sure do not want to script this.
Example code
Rem Open a connection in synchronous mode
Set Connection = Application.OpenConnection( "U9C [PUBLIC]", True)
Set Session = Connection.Children(0)
Rem Do something: Either fill out the login screen
Rem or in case of Single-Sign-On start a transaction.
Session.SendCommand( "/nbibs")

Extract file names from a File Explorer search into Excel

This has been bugging me for while as I feel I have few pieces of the puzzle but I cant put them all together
So my goal is to be able to search all .pdfs in a given location for a keyword or phrase within the content of the files, not the filename, and then use the results of the search to populate an excel spreadsheet.
Before we start, I know that this easy to do using the Acrobat Pro API, but my company are not going to pay for licences for everyone so that this one macro will work.
The windows file explorer search accepts advanced query syntax and will search inside the contents of files assuming that the correct ifilters are enabled. E.g. if you have a word document called doc1.docx and the text inside the document reads "blahblahblah", and you search for "blah" doc1.docx will appear as the result.
As far as I know, this cannot be acheived using the FileSystemObject, but if someone could confirm either way that would be really useful?
I have a simple code that opens an explorer window and searches for a string within the contents of all files in the given location. Once the search has completed I have an explorer window with all the files required listed. How do I take this list and populate an excel with the filenames of these files?
dim eSearch As String
eSearch = "explorer " & Chr$(34) & "search-ms://query=System.Generic.String:" & [search term here] & "&crumb=location:" & [Directory Here] & Chr$(34)
Call Shell (eSearch)
Assuming the location is indexed you can access the catalog directly with ADO (add a reference to Microsoft ActiveX Data Objects 2.x):
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim sql As String
cn.Open "Provider=Search.CollatorDSO;Extended Properties='Application=Windows'"
sql = "SELECT System.ItemNameDisplay, System.ItemPathDisplay FROM SystemIndex WHERE SCOPE='file:C:\look\here' AND System.Kind <> 'folder' AND CONTAINS(System.FileName, '""*.PDF""') AND CONTAINS ('""find this text""')"
rs.Open sql, cn, adOpenForwardOnly, adLockReadOnly
If Not rs.EOF Then
Do While Not rs.EOF
Debug.Print "File: "; rs.Collect(0)
Debug.Print "Path: "; rs.Collect(1)
rs.MoveNext
Loop
End If
Try using the next function, please:
Function GetFilteredFiles(foldPath As String) As Collection
'If using a reference to `Microsoft Internet Controls (ShDocVW.dll)_____________________
'uncomment the next 2 lines and comment the following three (without any reference part)
'Dim ExpWin As SHDocVw.ShellWindows, CurrWin As SHDocVw.InternetExplorer
'Set ExpWin = New SHDocVw.ShellWindows
'_______________________________________________________________________________________
'Without any reference:_____________________________________
Dim ExpWin As Object, CurrWin As Object, objshell As Object
Set objshell = CreateObject("Shell.Application")
Set ExpWin = objshell.Windows
'___________________________________________________________
Dim Result As New Collection, oFolderItems As Object, i As Long
Dim CurrSelFile As String
For Each CurrWin In ExpWin
If Not CurrWin.Document Is Nothing Then
If Not CurrWin.Document.FocusedItem Is Nothing Then
If left(CurrWin.Document.FocusedItem.Path, _
InStrRev(CurrWin.Document.FocusedItem.Path, "\")) = foldPath Then
Set oFolderItems = CurrWin.Document.folder.Items
For i = 0 To oFolderItems.count
On Error Resume Next
If Err.Number <> 0 Then
Err.Clear: On Error GoTo 0
Else
Result.Add oFolderItems.item(CLng(i)).Name
On Error GoTo 0
End If
Next
End If
End If
End If
Next CurrWin
Set GetFilteredFiles = Result
End Function
Like it is, the function works without any reference...
The above function must be called after you executed the search query in your existing code. It can be called in the next (testing) way:
Sub testGetFilteredFiles()
Dim C As Collection, El As Variant
Set C = GetFilteredFiles("C:\Teste VBA Excel\")'use here the folder path you used for searching
For Each El In C
Debug.Print El
Next
End Sub
The above solution iterates between all IExplorer windows and return what is visible there (after filtering) for the folder you initially used to search.
You can manually test it, searching for something in a specific folder and then call the function with that specific folder path as argument ("\" backslash at the end...).
I've forgotten everything I ever knew about VBA, but recently stumbled across an easy way to execute Explorer searches using the Shell.Application COM object. My code is PowerShell, but the COM objects & methods are what's critical. Surely someone here can translate.
This has what I think are several advantages:
The query text is identical to what you wouold type in the Search Bar in Explorer, e.g.'Ext:pdf Content:compressor'
It's easily launched from code and results are easily extracted with code, but SearchResults window is available for visual inspection/review.
With looping & pauses, you can execute a series of searches in the same window.
I think this ability has been sitting there forever, but the MS documentation of the Document object & FilterView method make no mention of how they apply to File Explorer.
I hope others find this useful.
$FolderToSearch = 'c:\Path\To\Folder'
$SearchBoxText = 'ext:pdf Content:compressor'
$Shell = New-Object -ComObject shell.application
### Get handles of currenlty open Explorer Windows
$CurrentWindows = ( $Shell.Windows() | Where FullName -match 'explorer.exe$' ).HWND
$WinCount = $Shell.Windows().Count
$Shell.Open( $FolderToSearch )
Do { Sleep -m 50 } Until ( $Shell.Windows().Count -gt $WinCount )
$WindowToSerch = ( $Shell.Windows() | Where FullName -match 'explorer.exe$' ) | Where { $_.HWND -notIn $CurrentWindows }
$WindowToSearch.Document.FilterView( $SearchBoxText )
Do { Sleep -m 50 } Until ( $WindowToSearch.ReadyState -eq 4 )
### Fully-qualified name:
$FoundFiles = ( $WindowToSearch.Document.Folder.Items() ).Path
### or just the filename:
$FoundFiles = ( $WindowToSearch.Document.Folder.Items() ).Name
### $FoundFIles is an array of strings containing the names.
### The Excel portion I leave to you! :D

Is there a resolution in VBA for the error Run-time error '429' when trying to connect to a website?

I am trying to create a VBA to download a google sheet into excel so I can compile stock market data daily. I would simply use power query for this but I am doing this on my personal laptop which is a mac and does not support power query. I am relatively new to coding so have been leaning on following online instructions. The instruction includes this:
Set objWebCon = CreateObject("MSXML2.XMLHTTP.3.0")
This line when ran creates an error message saying:
"
Run-time error '429':
ActiveX component can't create object
"
I think the issue lies within the fact that the instruction is based on a windows operating system. Any solution I've searched for is specific to windows operating systems.
Does anybody here know if I can change the "MSXML2.XMLHTTP.3.0" part of my code to fit it better to mac? Not sure if this is what needs to be done but any guidance would be super appreciated.
I attached my full code below but feel free to ignore it if not relavent. Thank you!!
Sub DownloadGoogleSheets()
Dim ShtUrl As String, Location As String, FileName As String
Dim objWebCon, objWrit As Object
'Sheet Url
ShtUrl = "https://docs.google.com/spreadsheets/d/1wpA_epxtlz96sxETqKttJwsy9Aubb15H8xslcSQ20T0/export?format=csv&id=1wpA_epxtlz96sxETqKttJwsy9Aubb15H8xslcSQ20T0" & gid = 1319327791
'Location
Location = ThisWorkbook.Path & "\" '/Users/[myName]/Desktop/Stock Analysis/n"
'FileName
FileName = "GoogleSheet.csv"
'Connection to Website
Set objWebCon = CreateObject("MSXML2.XMLHTTP.3.0")
'Writer
Set objWrit = CreateObject("ADODB.Stream")
'Connecting to the Website
objWebCon.Open "Get", ShtUrl, False
objWebCon.Send (ShtUrl)
'Once page is fully loaded
If objWebCon.Status = 200 Then
'Write the text of the sheet
objWrit.Open
objWrit.Type = 1
objWrit.Write objWebCon.ResponseBody
objWrit.Position = 0
objWrit.SaveToFile Location & FileName
objWrit.Close
End If
Set objWebCon = Nothing
Set objWrit = Nothing
End Sub

Dim As Dictionary. Compile Error: User-defined type not defined

Can you please assist with this problem?
Whenever I run this macro, it stops at:
Dim authResult As Dictionary
With an error message of:
Compile error: User-defined type not defined.
I have not used the dictionary type before and I am trying to re-use this code from a sample macro.
The aim of this script is to use excel to make rest calls to a website so that I can download historic data. I am currently stuck at the login section.
Sub Login()
Dim userName As String
Dim password As String
Dim apiKey As String
userName = "username"
password = "password"
apiKey = "key123"
'activityTextbox.Text = ""
'clearData
Dim authResult As Dictionary
Set authResult = restClient.authenticateAccount(userName, password, apiKey)
If Not authResult Is Nothing Then
'appendActivity "Connected"
' Configure Excel to pull streaming updates as often as possible
Application.RTD.ThrottleInterval = 0
' Uncomment for real-time prices - this is very CPU intensive
' Buffer interval defaults to 500ms
'Application.WorksheetFunction.RTD "IG.api.excel.RTD.IGApiRTDServer", "", "bufferInterval", "0"
' Set manual refresh to true from very remote locations
' Application.WorksheetFunction.RTD "IG.api.excel.RTD.IGApiRTDServer", "", "manualRefresh", "true"
' This will require manually calling refresh to update lighstreamer subscriptions, i.e.
' Application.WorksheetFunction.RTD "IG.api.excel.RTD.IGApiRTDServer", "", "refresh"
Dim maxPriceRequestsPerSecond As Double
maxPriceRequestsPerSecond = 0 ' all available updates
If restClient.streamingAuthentication(maxPriceRequestsPerSecond) Then
m_loggedIn = True
'populateWatchlists
'populateAccounts
'manualStreamingRefresh
'Else
' appendActivity "Lightstreamer connection failure"
End If
Else
MsgBox "Authentication failed"
End If
End Sub
Thanks in advance.
Cheers,
Joe
Add a reference to Microsoft Scripting Runtime as #YowE3k said:
In the VBA Editor:
Tools -> References
Find Microsoft Scripting Runtime
Check it
Click okay

Resources