I have a problem with SAP VA02 where I want to identify the row line in which a specific label is. In this case the label/text is "Cust. expected price".
I am trying to change the data next to this row, problem is that it is not always the same row, sometimes it is 16, 18, etc.
I am trying to find a way to loop through each row in column 2 in the structure, read the text, and find which row the label is in, then use the row as a variable to paste the price in the correct cell. I have pasted some functioning code below.
What I am doing is inputting the correct price here:
session.findById("wnd[0]/usr/tabsTAXI_TABSTRIP_ITEM/tabpT\05/ssubSUBSCREEN_BODY:SAPLV69A:6201/tblSAPLV69ATCTRL_KONDITIONEN/txtKOMV-KBETR[3,16]").Text = Price
My main question is how to read what text is in each cell for example session.findById("wnd[0]/usr/tabsTAXI_TABSTRIP_ITEM/tabpT\05/ssubSUBSCREEN_BODY:SAPLV69A:6201/tblSAPLV69ATCTRL_KONDITIONEN/txtKOMV-KBETR[2,16]")
I can probably figure out the rest from there. I haven't been able to find much regarding this specific structure, any input is appreciated. I will also post a screenshot of the page for reference. Thank you!
Sub OrderRelease()
Dim Order As String
Dim RowCount As Integer
Dim Item As Integer
Dim sh As Worksheet
Dim rw As Range
Dim Sroll As Integer
Dim Price As Double
On Error Resume Next
RowCount = 0
Set sh = ActiveSheet
For Each rw In sh.Rows
If sh.Cells(rw.Row, 6).Value = "" Then
Exit For
End If
RowCount = RowCount + 1
Next rw
If Not IsObject(SAPGuiApp) Then
Set SapGuiAuto = GetObject("SAPGUI")
Set SAPGuiApp = SapGuiAuto.GetScriptingEngine
End If
If Not IsObject(Connection) Then
Set Connection = SAPGuiApp.Children(0)
End If
If Not IsObject(SAP_session) Then
Set session = Connection.Children(0)
End If
If IsObject(WScript) Then
WScript.ConnectObject SAP_session, "on"
WScript.ConnectObject SAPGuiApp, "on"
End If
session.findById("wnd[0]").maximize
session.findById("wnd[0]/tbar[0]/okcd").Text = "/nva02"
session.findById("wnd[0]").sendVKey 0
For i = 2 To RowCount
Order = Cells(i, "F")
session.findById("wnd[0]/usr/ctxtVBAK-VBELN").Text = Order
session.findById("wnd[0]/usr/ctxtVBAK-VBELN").caretPosition = 9
session.findById("wnd[0]").sendVKey 0
session.findById("wnd[1]/tbar[0]/btn[0]").press
Continue:
Item = Cells(i, "G") / 10 - 1
Scroll = Item - 1
Price = Cells(i, "H")
Set sub = session.findById("wnd[0]/usr/tabsTAXI_TABSTRIP_OVERVIEW/tabpT\02/ssubSU" _
& "BSCREEN_BODY:SAPMV45A:4401/subSUBSCREEN_TC:SAPMV45A:4900")
Set tbl = sub.findById("tblSAPMV45ATCTRL_U_ERF_AUFTRAG")
tbl.verticalScrollbar.Position = Scroll
tbl.getAbsoluteRow(Item).Selected = True
tbl.findById("txtVBAP-POSNR[0,8]").SetFocus
tbl.findById("txtVBAP-POSNR[0,8]").caretPosition = 4
sub.findById("subSUBSCREEN_BUTTONS:SAPMV45A:4050/btnBT_PKON").press
Set tbl2 = session.findById("wnd[0]/usr/tabsTAXI_TABSTRIP_ITEM/tabpT\05/ssubSU" _
& "BSCREEN_BODY:SAPLV69A:6201/tblSAPLV69ATCTRL_KONDITIONEN")
tbl2.verticalScrollbar.Position = 8
'The below line is what I need to find. In this case, Cust. expected price would be 2,16,
'but I have not found a way to actually read the text in that cell.
tbl2.findById("txtKOMV-KBETR[3,16]").Text = Price
tbl2.findById("txtKOMV-KBETR[3,16]").SetFocus
tbl2.findById("txtKOMV-KBETR[3,16]").caretPosition = 16
session.findById("wnd[0]/usr/tabsTAXI_TABSTRIP_ITEM/tabpT\11").Select
session.findById("wnd[0]/usr/tabsTAXI_TABSTRIP_ITEM/tabpT\11/ssubSU" _
& "BSCREEN_BODY:SAPMV45A:4456/cmbVBAP-ABGRU").Key = " "
session.findById("wnd[0]/tbar[0]/btn[3]").press
session.findById("wnd[0]/usr/btnBUT2").press
session.findById("wnd[1]/tbar[0]/btn[0]").press
session.findById("wnd[0]").sendVKey 0
If Cells(i, "F") = Cells(i + 1, "F") Then
i = i + 1
GoTo Continue
End If
session.findById("wnd[0]").sendVKey 11
session.findById("wnd[1]/tbar[0]/btn[0]").press
session.findById("wnd[1]/usr/btnSPOP-VAROPTION1").press
Next i
End Sub
Here is how to refer to a value of a cell in a given row and a given column, which are both provided in variables:
row = 0
column = 1
cellText = session.findById(".../tblXXXXX/columnFieldName[" & column & "," & row & "]").Text
Another solution is to use the method GetCell on the Table Control object:
cellText = session.findById(".../tblXXXXX").GetCell(row,column).Text
NB: notice that row and column arguments are switched.
To know what values to use for ".../tblXXXXX/columnFieldName[...], the easiest way is to record a script, by simply moving the cursor to the desired column. The generated script will return something like that (test with the demo program DEMO_DYNPRO_TABCONT_LOOPFLIGHTS):
session.findById("wnd[0]/usr/tblDEMO_DYNPRO_TABCONT_LOOPFLIGHTS/ctxtDEMO_CONN-CITYFROM[2,1]").setFocus
session.findById("wnd[0]/usr/tblDEMO_DYNPRO_TABCONT_LOOPFLIGHTS/ctxtDEMO_CONN-CITYFROM[2,1]").caretPosition = 1
The row number corresponds to the order among the visible rows, starting from 0 (0 = first visible row). The last visible row has the number equals to the Table Control property VisibleRowCount minus 1. The rows which are not visible (above and below) can be accessed by making your script scroll vertically, for more information about scrolling programatically see below chapter.
The column number is based on the order of columns shown in the Table Control, whatever the columns are immediately visible or visible after horizontal scrolling. The script doesn't need to perform horizontal scrolling to read the values of non-visible columns. 0 is the leftmost column, and the rightmost column has the number equals to the two properties of the Table Control Columns.Count minus 1.
The list of columns and their order may vary according to the active Table Control configuration. You may wish to determine the column number based on the column name at run time, for that see below chapter.
There may be other columns proposed via the Table Control administrator function, with the "hidden" checkbox selected. SAP GUI Scripting completely ignores these columns. If you need to work with them, you must call the table control method ConfigureLayout to display the administrator screen, and then you can work with the settings as you do with any other screen.
Scrolling the rows
For a Table Control, SAP GUI Scripting knows only the data in the lines which are currently visible on the screen, because for performance reason the backend ABAP program sends only these lines to the frontend. SAP GUI Scripting can't know the values from the invisible lines. It's required that the script scrolls vertically to obtain the other rows. Attention, scrolling means the reloading of the whole screen, so the screen elements need to be re-instantiated. The following example scrolls the whole list to display all the values in the first column (use of the demo program DEMO_DYNPRO_TABCONT_LOOPFLIGHTS):
Set tbl = session.findById("wnd[0]/usr/tblDEMO_DYNPRO_TABCONT_LOOPFLIGHTS")
' Make the first row visible (show the top of the list) -> that calls the back-end system and screen is reloaded.
' ATTENTION: when the back-end is called, to continue working with screen elements, they must be re-instantiated.
tbl.VerticalScrollbar.Position = 0
TextsOfAllCellsInColumnZero = ""
Do While True
' Re-instantiate the Table Control element (mandatory each time the back-end is called)
Set tbl = session.findById("wnd[0]/usr/tblDEMO_DYNPRO_TABCONT_LOOPFLIGHTS")
visibleRow = 0
currentScrollbarPosition = tbl.VerticalScrollbar.Position
While visibleRow < tbl.VisibleRowCount And currentScrollbarPosition <= tbl.VerticalScrollbar.Maximum
TextsOfAllCellsInColumnZero = TextsOfAllCellsInColumnZero & tbl.GetCell(visibleRow,0).Text & Chr(10)
visibleRow = visibleRow + 1
currentScrollbarPosition = currentScrollbarPosition + 1
Wend
If currentScrollbarPosition > tbl.VerticalScrollbar.Maximum Then
Exit Do
End If
tbl.VerticalScrollbar.Position = currentScrollbarPosition
Loop
MsgBox TextsOfAllCellsInColumnZero
Note that this example is suitable to a small number of pages. In many other situations, there are many more pages, for an action like searching a line containing a given value, it would be much more performing to click an existing button to perform a back-end search of this value. The right page would be immediately be displayed.
Determine the column number from the column name at run time
As explained above, the column number may vary depending on the order of columns and on hidden columns. If they vary in an undetermined way at run time, the following code allows to determine the column number based on the column name (note that the lower case prefix of the field name is to be removed, like "ctxt" in "ctxtDEMO_CONN-CITYFROM"), but it works only if there's at least 1 row (no solution found if it's needed when the Table Control is empty):
Set tbl = session.findById("wnd[0]/usr/tblDEMO_DYNPRO_TABCONT_LOOPFLIGHTS")
column = GetColumnNumberByName(tbl,"DEMO_CONN-CITYFROM")
msgbox tbl.GetCell(row,column).text
Function GetColumnNumberByName( TableControl, ColumnName )
If TableControl.Rows.Count > 0 Then
For i = 0 To TableControl.Columns.Count - 1
If TableControl.Columns(i)(0).Name = ColumnName Then
GetColumnNumberByName = i
Exit Function
End If
Next
End If
GetColumnNumberByName = -1
End Function
Appendix
For more information, please refer to the documentation of the "GuiTableControl Object" in the SAP Library.
NB: if you look at other questions, be aware that a Table Control (GuiTableControl) is completely unrelated to a Grid View (GuiGridView), so don't be confused.
Related
I have built a macro that connects to SAP, do its thing, gets the response and writes it down on the spreadsheet. The issue is, before the script could do it, I need to have the data by cluster.
For understanding, I have a list of customers with their codes and I have built a master key for each of them, because before I input it to SAP, I need to filter the customers by master key codes that are equal so SAP will accept it. But on the source file I'll have multiples master key codes.
I made a pivot table and put the master key on the columns, so I can see customers code by master key.
With that in hand I filter by master key and I have the specific customer codes for it, and now I can send it to SAP. And repeat for all master keys on the file.
This information will change every time from the sales department, so the master keys will change everytime too.
What I need to do is get all the master keys from the file (done that) and, the place I'm stuck, use it to hide and show one at time inside a loop.
For example (MK stands for Master Key):
Customer MK1 MK2 MK3 MK4
X 1
Y 1
Z 1
A 1
So I filter MK1, get customer code for it, run SAP thing, get back to the pivot, filter next existing code, get the customers code, run SAP and do all over again.
If Range("A2") <> "" Then
Range("A1").Select
Selection.End(xlDown).Select
ActiveCell.Offset(-1, 1).Select
Range(Selection, Selection.End(xlUp)).FillDown
FILTRO2 = ActiveCell
Range("A1").Select
Do While ActiveCell <> ""
FILTRO = ActiveCell
Sheets("Dinâmica").Activate
ActiveWorkbook.RefreshAll
With ActiveSheet.PivotTables("Tabela dinâmica2").PivotFields("Chave")
.PivotItems(Split(FILTRO2.Value, ",")).Visible = False
.PivotItems(FILTRO).Visible = True
End With
As seen on above code, I'm on a dead end. Isn't working at all.
You can switch each single "masterkey" visible by Pivotitem.Visible.
Please be aware, that always at least 1 pivotitem has to remain visible.
The resulting visible "customers" can be read from the PivotField.DataRange, which represents all visible pivotitems of that pivotfield. You can address that range cell by cell or assign its value to an array.
Private Sub FilterMasterkeys()
Dim pt As PivotTable
Dim pfCustomer As PivotField
Dim pfMasterkey As PivotField
Dim i As Long
Dim c As Range ' a cell
Set pt = ActiveWorkbook.Sheets("Dinâmica").PivotTables("Tabela dinâmica2")
pt.RefreshTable
Set pfCustomer = pt.RowFields(1)
Set pfMasterkey = pt.ColumnFields("Chave")
' hide all masterkeys except first
pfMasterkey.PivotItems(1).Visible = True
For i = 2 To pfMasterkey.PivotItems.Count
pfMasterkey.PivotItems(i).Visible = False
Next i
' set each masterkey visible and get corresponig customers
For i = 1 To pfMasterkey.PivotItems.Count - 1
pfMasterkey.PivotItems(i).Visible = True
Debug.Print "Masterkey " & pfMasterkey.PivotItems(i).Caption & " is used by ",
For Each c In pfCustomer.DataRange.Cells
Debug.Print c.Value,
Next c
Debug.Print
pfMasterkey.PivotItems(i + 1).Visible = True
pfMasterkey.PivotItems(i).Visible = False
Next i
' get the last one also
Debug.Print "Masterkey " & pfMasterkey.PivotItems(i).Caption & "is used by ",
For Each c In pfCustomer.DataRange.Cells
Debug.Print c.Value,
Next c
Debug.Print
End Sub
If you need all corresponding customers as array, use this after you have switched the next masterkey visible:
Dim arrCustomers() As Variant
arrCustomers = pfCustomer.DataRange.Value
I am working with a VB macro. Essentially what I am trying to do is for the macros to read the input and first determine whether or not a cells ID number matches the one in the row. Example: If row 1 has an ID of 1122 and rows 2,3,4 and 5 all match, I want the macro to read that and create a count in the NbrOfA cell. Once it realizes that there is not an ID match it moves on to the next ID and looks for matches of that ID number and continues to create a count. While it is doing this, I also need it to read from another column that has specific strings such as "open", "closed" ect. read that input, and create a separate row titled NbrofOpenA. Once it runs out of data, I then want to have a singular cell that shows the number of actions (NbrOfA) that match the ID number as well as the number of open actions (NbrOfOpenA).
Currently I receive the error: “compile error: sub or function not defined” highlighting the Set Cell(Sheet2.Cells(FirstRowOfI, 23) = NbrOfA
Attached in the excel sheet attached it shows 2 cells deleted. They will not actually be deleted, just wanted to give an idea of what I was looking for
Sub ACount()
Dim FirstRowofI
Dim NbrOfA as Integer
Dim NbrOfOpenA as Integer
Row = 2
Set FirstRowofI = (Sheet2.Cells.Range(Row, 14))
NbrOfA = 0
NbrOfOpenA = 0
If (Sheet2.Cells(Row, 14).Value <> "") Then
NbrOfA = 1
If (Sheet2.Cells(Row, 22) <> "Closed") Then
NbrOfOpenA = 1
Set Row = FirstRowofI
Row = Row + 1
Do While (Sheet2.Cells(Row, 14) = (Sheet2.Cells(FirstRowofI, 14)))
NbrOfOpenA = NbrOfOpenA + 1
If (Sheet2.Cells(Row, 22) <> "Closed" Then
NbrOfOpenA = NbrOfOpenA + 1
Range(Row).EntireRow.Delete
Return
End If
Set Cell(Sheet2.Cells(FirstRowofI, 23)) = NbrOfA
Set Cell(Sheet2.Cells(FirstRowofI, 24)) = NbrOfOpenA
Loop
End Sub
[1
Do you need VBA? You can easily achieve what you're looking for with formulas, heck even a Pivot Table! Here's an example with formulas:
I am displaying a listbox from which the user may select lines.
The code then reads from the spreadsheet cells associated with that list item and set a number of text variables. These are then concatenated and stored in to an array.
Finally, the array is read and the strings copied to the Clipboard.
However, when I try to use CTRL-v to paste the values in to an email/text file, no text is entered (the Clipboard appears to be empty).
Below is the salient code...
NB. Code based on a 3-line selection from the Listbox
Dim LinesOfData() As String
'Other code that doesn't have direct relevance.. builds the strings
Set MyData = New DataObject
MyData.Clear
Dim record As Variant
Dim Indentifier As String
j = 0
For Each record In LinesOfData
Indentifier = "FormatID" & CStr(j)
Debug.Print record
MyData.SetText record, Indentifier
MyData.PutInClipboard
MyData.GetFromClipboard
'
' Test code only
'
If j = 0 Then 'First pass, only 1 record to check
S = MyData.GetText("FormatID0")
Debug.Print S 'Shows correct value
ElseIf j = 1 Then '2nd pass, 2 records to check
S = MyData.GetText("FormatID0")
Debug.Print S 'Shows correct value
S = MyData.GetText("FormatID1")
Debug.Print S 'Shows correct value
ElseIf j = 2 Then
S = MyData.GetText("FormatID0")
Debug.Print S 'Shows correct value
S = MyData.GetText("FormatID1")
Debug.Print S 'Shows correct value
S = MyData.GetText("FormatID2")
Debug.Print S 'Shows correct value
End If
' If I go and try to paste in another appl'n at this point, nothing!
j = j + 1
Next record
' End code
Now it doesn't matter if I exit the particular userform, or the application, I still have an empty Clipboard (despite being able to read the data from the Clipboard within the code).
As you might expect, I kept looking for an asnwer and indeed I found it.
THIS IS A WINDOWS BUG!
Apparently, this occurs when you have 2 or more Explorer windows open (since Windows 8) and I had just that the entire time I was testing. Once I had only the one window open, viola! it worked.
Not sure if this is allowed. but here is the thread that alerted me, with code to achieve the result using API calls, provided by, Debaser at the end of the post.
https://chandoo.org/forum/threads/clipboard-copy-vba-code-not-working-in-windows-10.37126/
Hopefully, others will find this useful.
I am writing a script from VBA and using a recorded action in SAP GUI screen. I want the script to enter a certain transaction paste a certain value (contract number) and then go to a certain row in a table double click it and do a certain action inside.
I use a while loop in VBA and in an Excel sheet I store the contract number, the number of the row on which the script should double click and then paste the value I need once it enters. It should do it until cells in column a are empty.
You can see in the VBS part of the code below that I tried to replace the row number with the variable "row", but the code does not accept that.
If I leave a number there it actually does what I want as long as the number is the correct one for what I try to do.
Can someone help me with this one? I go around this with if statements should the number of possibilities for the row number is limited, but that could mean to have a very long code, and potentially something can be missed.
Is it possible to make the row number refer to a variable in the code.
I already tried to declare the variable in VBS but it does not work as well.
Code:
'We declared the parameter for the while function in excel
Dim cont As String
Dim row As Integer
Dim rep As String
Dim j As Integer
j = 2
With ThisWorkbook
While Cells(j, 1) <> ""
cont = Cells(j, 1).Value
row = Cells(j, 3).Value
rep = Cells(j, 4).Value
Set SapGuiAuto = GetObject("SAPGUI")
Set SAPApp = SapGuiAuto.GetScriptingEngine
Set SAPCon = SAPApp.Children(0)
Set Session = SAPCon.Children(0)
If IsObject(WScript) Then
WScript.ConnectObject Session, "on"
WScript.ConnectObject Application, "on"
End If
Session.findById("wnd[0]").maximize
Session.findById("wnd[0]/usr/ctxtVBAK-VBELN").Text = cont
Session.findById("wnd[0]/usr/ctxtVBAK-VBELN").caretPosition = 8
Session.findById("wnd[0]").sendVKey 0
Session.findById("wnd[0]/usr/tabsTAXI_TABSTRIP_OVERVIEW/tabpT\01/ssubSUBSCREEN_BODY:SAPMV45A:4426/subSUBSCREEN_TC:SAPMV45A:4908/tblSAPMV45ATCTRL_U_ERF_KONTRAKT/ctxtVBAP-KDMAT[5,row]").SetFocus
Session.findById("wnd[0]/usr/tabsTAXI_TABSTRIP_OVERVIEW/tabpT\01/ssubSUBSCREEN_BODY:SAPMV45A:4426/subSUBSCREEN_TC:SAPMV45A:4908/tblSAPMV45ATCTRL_U_ERF_KONTRAKT/ctxtVBAP-KDMAT[5,row]").caretPosition = 6
Session.findById("wnd[0]").sendVKey 2
Session.findById("wnd[0]/usr/tabsTAXI_TABSTRIP_ITEM/tabpT\10").Select
Session.findById("wnd[0]/usr/tabsTAXI_TABSTRIP_ITEM/tabpT\10/ssubSUBSCREEN_BODY:SAPMV45A:4454/ctxtVBAP-KDMAT").Text = rep
Session.findById("wnd[0]/usr/tabsTAXI_TABSTRIP_ITEM/tabpT\10/ssubSUBSCREEN_BODY:SAPMV45A:4454/ctxtVBAP-KDMAT").SetFocus
Session.findById("wnd[0]/usr/tabsTAXI_TABSTRIP_ITEM/tabpT\10/ssubSUBSCREEN_BODY:SAPMV45A:4454/ctxtVBAP-KDMAT").caretPosition = 3
Session.findById("wnd[0]/tbar[0]/btn[11]").press
j = j + 1
Wend
End With
You do not use the variable row as a variable instead you use the string "row". Have a look at rule #5 here . "row" does not mean anything to the SAPGUI.
Try this
session.findById("wnd[0]/usr/tabsTAXI_TABSTRIP_OVERVIEW/tabpT\01/ssubSUBSCREEN_BODY:SAPMV45A:4426/subSUBSCREEN_TC:SAPMV45A:4908/tblSAPMV45ATCTRL_U_ERF_KONTRAKT/ctxtVBAP-KDMAT[5," & CStr(Row) & "]").SetFocus
You need to do this for the second string as well.
You also use the wrong key word. This is not SAP ABAP, this is SAPGUI scripting.
I have working code which selects single items from a slicer, however it doesn't work for multiple items.
The selection is set up by reading which cells in a range are in bold, and populating an array of strings, STP(46), populating up until STP(k). This works fine.
Then the code is supposed to deselect all items in the slicer which aren't in STP, and select those which are. This works for one selection but not for multiple selections - it erroneously selects all items up until the last item to be selected.
With ActiveWorkbook.SlicerCaches("Slicer_STP_Name")
For i = 1 To .SlicerItems.Count
For j = 1 To k
If .SlicerItems(i).Selected And .SlicerItems(i).Caption <> STP(j) Then .SlicerItems(i).Selected = False
Next j
Next i
For i = 1 To .SlicerItems.Count
For j = 1 To k
If .SlicerItems(i).Caption = STP(j) Then .SlicerItems(i).Selected = True: Exit For
Next j
Next i
End with
So instead of selecting, say, the 2nd and 4th item in the slicer, it selects the 1st, 2nd, 3rd, 4th, and deselects the rest.
I need to use a looped technique like this because I need to be able to use this code with multiple slicers with different cache names but the same list of items.
I've looked everywhere, and the code above is even from a solution from another question on here. Any help greatly appreciated!
You can use a dictionary to make the process a little more smooth
With ActiveWorkbook.SlicerCaches("Slicer_test_id")
Dim i
For i = 1 To .SlicerItems.Count
If .SlicerItems(i).Selected And Not stp.Exists(.SlicerItems(i).Caption) Then
.SlicerItems(i).Selected = False
End If
Next i
For i = 1 To .SlicerItems.Count
If stp.Exists(.SlicerItems(i).Caption) Then
.SlicerItems(i).Selected = True
End If
Next i
End With
I'm not totally clear on why you need the first loop. I'm reading "Then the code is supposed to deselect all items in the slicer which aren't in STP, and select those which are." as "select only those otems in STP and deselect all others" which this reduced code will do:
With ActiveWorkbook.SlicerCaches("Slicer_test_id")
Dim i
For i = 1 To .SlicerItems.Count
.SlicerItems(i).Selected = stp.Exists(.SlicerItems(i).Caption)
Next i
End With
populating the Dictionary is super easy
Dim stp As New Dictionary
stp.Add "73148", "73148"
stp.Add "73150", "73150"
stp.Add "73159", "73159"
You need to reference the Microsoft Scripting Runtime
note that if you don't see Microsoft Scripting Runtime in the list you can Browse to C:\Windows\SysWOW64\scrrun.dll