How to select current row in SAP GUI Grid View with VBA Macro? - vba

I am trying to automate a repetitive task in the SAP GUI. I need to search for an order number, select the row that the order number is in and then click a button to complete the task. I have recorded a macro which gives me:
session.findById("wnd[0]").maximize
session.findById("wnd[0]/usr/cntlCONTAINER/shellcont/shell").pressToolbarButton "&FIND"
session.findById("wnd[1]/usr/txtGS_SEARCH-VALUE").text = "4521305207"
session.findById("wnd[1]/usr/txtGS_SEARCH-VALUE").caretPosition = 10
session.findById("wnd[1]/tbar[0]/btn[0]").press
session.findById("wnd[1]/tbar[0]/btn[12]").press
session.findById("wnd[0]/usr/cntlCONTAINER/shellcont/shell").currentCellColumn = ""
session.findById("wnd[0]/usr/cntlCONTAINER/shellcont/shell").selectedRows = "2894"
session.findById("wnd[0]/tbar[1]/btn[14]").press
session.findById("wnd[1]/usr/chk[1,6]").selected = true
The line:
session.findById("wnd[1]/usr/txtGS_SEARCH-VALUE").text = "4521305207"
Corresponds to the order I want to search, but if I change this value it still tries to process the same order that the macro was recorded on, I'm assuming because of the line:
session.findById("wnd[0]/usr/cntlCONTAINER/shellcont/shell").selectedRows = "2894"
Does anyone know how I would go about finding the number of the row which corresponds to the outcome of the SEARCH-VALUE and then using that as the .selectedRows = ""?

First of all I'd really recommend you add a reference to the native SAP library. Go to your VBA Editor, click Tools, then References, then Browse, and find this file: "C:\Program Files\SAP\FrontEnd\SAPgui\sapfewse.ocx". Add it, and now you'll have types and libraries and coding for SAP will be a lot easier, safer, and slightly faster (Variant types in VBA impose a tiny overhead that in this case is totally unnecessary). Get familiar with this new library if you are going to do any SAP scripting more than once.
Second, about this problem, what you have is a shell, of type GuiShell, which inherits from GuiGridView. GuiGridView looks like a table, a classic Excel-like set of rows and columns. In your transaction, is showing you a big list of orders, in which you go click the "Find" button, put the order you're looking for, and then close the Search Window. Back to your (Grid)Shell, this cell has been selected (Grid has properties SelectedCells, SelectedRows, SelectedColumns that get all set when you go find something), but then you go and modify the value of SelectedRows to a specific one.
So yeah, upon find, a cell has been selected, so all you need is to query its row and then assign it where you need:
Dim numrRow As Long
numrRow = session.FindById("wnd[0]/usr/cntlGRID1/shellcont/shell").CurrentCellRow
session.FindById("wnd[0]/usr/cntlGRID1/shellcont/shell").SelectedRows = numrRow
where "thisShell" is however you do to find a reference to the Shell (session.findByID("blabla") for example, but I'd advise to reduce all the findByID's, they're very slow and type-unsafe).
If you need help about this SAP libraries, feel free to maybe make some new post and ping me on the comments about it.

Related

Is it possible to copy the contents of the Immediate window in Excel VBA?

Question
I would like to know whether it is possible to copy or extract the contents of the Immediate window in Excel VBA, so I can store it somewhere. I already know you can copy and paste from the window manually, I am interested in whether it is possible to do it with code (preferably in VBA itself).
Explanation
Questions similar to this have been asked a few times on SO (e.g. this and this), no-one has given a definitive answer and I was unable to find one from a quick search. Most of the time, answers respond asking why anyone would want to do that, and give ways to get around it in the circumstances provided in the question (e.g. how to output messages to a file or cell range). Given this, I have thought of a couple of example scenarios where someone might want to do this and cannot (easily) get around it.
Example scenarios
A) I am developing a large workbook including a large set of macros, debugging as I go. This includes using debug.print statements and directly querying in the Immediate window, e.g. ? myVar. At the end of the session, I would like to be able to automatically copy the contents of the immediate window into a log file, so I know what happened during my debug session afterwards. This could, for example, make use of the Workbook_BeforeClose event procedure.
B) I am working with two workbooks that contain VBA projects - one I can't edit, another that I can and am working on. The one I can't edit outputs debug information to the immediate window via debug.print that I would like to store somewhere (but I don't really care where).
Disclaimer
I ask this question purely out of curiosity. I suspect I already know the answer (it's not possible to do this), but I am not sure.
Yes-- but not with control-c .... select what you need and then drag and drop
1-Create a sheet named "debug.print"
2-Hide it:
Sheets("debug.print").Visible = 2 'xlSheetVeryHidden, only can be visible with vba
3-Create this function:
Function debug_print(c As String)
ThisWorkbook.Sheets("debug.print").[a1048576].End(xlUp).Offset(1).Value = c
Debug.Print c
End Function
4-Replace your codes "debug.print" to "debug_print"
5-Example:
Sub blablabla()
debug_print "i am doing whatever"
End Sub
But: Using "?" in the immediate window will not save
There is many ways to export this as file now like CSV, TXT, save in SQL etc...

Trouble with Copying VBA Code

I've been working on an independent project for a client of mine. They wanted to produce a button that, upon the user-click, it would open up a user-form and have a variety of macro-related options to choose from: a drop-down list, checkbox, option select button, etc.
I created a test formula and submitted it to the client; they enjoyed it thoroughly and decided to sent me a file to 'copy & paste' my original code within their excel file.
Problem is; because I'm a tad bit inexperienced with VBA I've run into a problem where once I click the button - the user form doesn't show up.
Below is a Dropbox link of the original file I created and it's original code; as well as the file that I am trying to copy.
Any help would be all welcome and appreciated.
Link to dropbox: https://www.dropbox.com/sh/l1t37lz8uritrua/AAAdWPGvw0GDZ6hW4SwmbBdRa?dl=0
OriginalProject.xlsm has a form named honor_roll_form which contains 100 lines of code.
CopyOfOriginal.xlsm has a form named UserForm1 which contains no useful code.
I do not believe there is any method of directly copying user forms from one workbook to another. Instead
Within VB Editor of OriginalProject.xlsm, select honor_roll_form.
Click File then Export File and save the form on your desktop or where ever you like.
You will now have two files on your desktop; one with an extension of frm and one with an extension of frx.
Within VB Editor of CopyOfOriginal.xlsm, click File then Import file.
Import honor_roll_form.frm
When I try clicking button "Honor Roll", I get "Method or data member not found" for project1Box. I will investigate after dinner (18:57 here) unless you tell me you already know why I am getting this error.
Extra comments in response to request from OP
It is late here but I have started looking down sub execute_button_Click within the second CopyOfOriginal.xlsm. I will comment on what I see even if it is not directly relevant to the non-execution of the macro.
If you open the VB Editor and look on the left you will see the Project Explorer. Near the top you will see:
Microsoft Excel Objects
Sheet1 (Sheet1)
I have always found this confusing. The first “Sheet1” is Excel’s Id for the worksheet and cannot be changed. The second “Sheet1” is the default name for the worksheet which can be changed. You can write Sheet1.Range("A1") or Worksheets("Sheet1").Range("A1"). That is: you can reference a worksheet by its Id or its name. You have named a variable of type Worksheet as Sheet1. Using Excel’s names as variable names can lead to bizarre errors so it is important to avoid doing anything like this.
It is better to always use meaningful names. At the moment, you know what Sheet1 means but if you come back to this macro in six or twelve months will you remember. I would use a variable as you have but I would name it WshtCis208 or WshtVBAProg or something similar.
Set ID = Range(Sheet1.Cells(2, 1), Sheet1.Cells(52, 1)) could be written as:
With WshtCis208
Set ID = Range(.Cells(2, 1), .Cells(52, 1))
End With
Using With statements produces faster code and, almost always, code that it easier to read.
“52” is the current bottom row for this table. Will you amend the macro for them every time they add or remove a student? There are several techniques for finding the last row, none of which is perfect in every situation. The technique that is the most convenient most of the time is:
Const ColCis208Id as Long = 1
Const ColCis208MidTermExam as Long = 5
Dim RowCis208Last as Long
RowCis208Last = .Cells(.Rows.Count, ColCis208Id).End(xlUp).Row
At the moment, column 1 is the Id column. It is perhaps unlikely that the Id column will move but it is very likely that some of the others columns will move when some new column is identified as useful. Do you want to scan the code trying to decide which 5s refer to the MidtermExam column when a Project3 column is added?
Constants allow you to name literals that might change. It makes your code easier to read and saves so much pain when a value changes.
.Rows.Count gives the number of rows in a worksheet for the current version of Excel so .Cells(.Rows.Count, ColCis208Id) identifies the bottom cell of column 1. End(xlUp).Row says go up until you hit a cell with a value and returns its row number. It is the VBA equivalent of Ctrl+Up.
The next statement subjectCount = … fails because projectBox does not exist on the form. You have changed the captions but not the names.
As far as I can see the form fails to execute because you have started updating it but have not finished.

Load only select sections of Code in QlikView

I have a set of code that is rather large in Qlik. I have dates that are defined at the start of the script
i.e.
Let vBDate = Date(Date#('01/01/2015','MM/DD/YYYY'),'MM/DD/YYYY');
Let vEDate = Date(Date#('12/31/2015','MM/DD/YYYY'),'MM/DD/YYYY');
The entire code runs financial numbers based on a specific data source. Originally I had a version of this QVW for each data source. However, as often goes with financials the QVW constantly needs to be refined. So I merged them all into one code. I broke the Code up into different tabs so I can turn off the sources I don't want.
What I want to do is try to se a variable, either in the code, like this,
Let vROIType = 'Vendor';
or using the method answered in my first attempt at this question where the variable is defined on the designer side using a button.
The hope is that when the variable is set, then only the code associated with that variable will run in the reload and the other code will be skipped.
In my research I tried to create functions in the script and use code to call them, however the call would always error out. I also read about QVDs but many of my date variables are defined at the start of running it, and the QVD needs to be pre-run.
Any help would be appreciated.
As long as you wrap the appropriate sections of the script in the conditionals properly, this should accomplish it:
Create a variable via the Variable Overview window (ctrl + alt + v) like you mentioned to reference the correct script to be reloaded (vROIType)
Create a button or text box and set an action to change the value of the vROIType variable. You can just make two buttons so you can select the correct data source by clicking the appropriate button.
Either reload via the menu or create another text box/button with an action to reload your script.
Most importantly, use conditionals in your script to selectively run the correct portions based on the vROIType variable.
if vROIType = 'Vendor' then
Everything in the script you want run when the source is `Vendor`.
elseif vROIType = 'SomeOtherVendor' then
Everything in the script you want run when the source is ....
end if;
Upon reload, the script will look at the vROIType variable and use that to determine whether or not to run parts of the script. Here's a link to a simple example you can try if you have the paid version of Qlikview, otherwise it'll yell at you that you can't open third party .qvw's.

Moving the code one coloumn to the left

I wrote a code and now my excel template has changed (the first column is no longer in need), is there a way to move the entry code 1 column to the left instant of correcting it step by step?
For example(This is the old code):
.Range("I1:J1") = Array("CHECK", "KEY")
Now I need to change it to:
.Range("H1:I1") = Array("CHECK", "KEY")
But it's a very long code and I want to know if there's any way do to it easier.
Thanks.
I'd suggest simply using the built in Search & Replace function ( Ctrl + f ). Put something like
.Range("I1:J1")
into the "Find What" field and
.Range("H1:I1")
into the "Replace With" field.
If you're really lazy you could hit the Replace All button, but this can be dangerous as in possibly changing parts you didn't want to change. However, using the Replace button and going through all entries can be fairly fast even in a longer code, and this way you can check with each entry if it's really correct to change it.

Excel VBA: Resetting spreadsheet count

I have a excel VBA macro that dynamically generates and deletes spreadsheets based on user input. However, when I open the VBA IDE, it seems that although I am naming my spreadsheets in the subs that create/delete them, the overall count is still increasing.
For example, depending on how far into execution my program is, under the "Microsoft Excel Objects" folder in my current project, the spreadsheets in the current workbook could look something like
Sheet101(Sheet3)
Sheet103(Sheet2)
Sheet104(Sheet1)
Or
Sheet81(Inputs)
Sheet83(Date Adjustment Interpolation)
Sheet84(Pricing)
Sheet85(Comparison)
No matter if I delete the rest of them and add one, it still picks up where the last highest one left off.
I don't know how many times this macro will be run and I'd feel a lot better about putting it out there if I could reset this annoying tally on the number of spreadsheets that have ever been generated, since I don't know for sure where excel will cut me off. Plus it's just annoying.
My Question:
I would like to know how to alter that spreadsheet number, or at least what the relevant object is for doing so.
Thanks!
Thanks to #dijkay s suggestion on code names, I've found some code to accomplish this.
ThisWorkbook.VBProject.VBComponents("Sheet1").name = "test"
Will change the code name of Sheet1 to test, so in the Excel Objects folder, it will appear as test(Sheet1) for example.
This option, however, requires messing around with some trust/security settings in each individual excel client running the macro, which is unsuitable for my purposes, unfortunately. You can also change the value manually by changing the (Name) property directly in the IDE through the properties window.
here are some ideas you can try...
Sheets(x).Name = "Sheet" & x
or (assuming in this example, 'Sheet3' doesn't already exist:
Set Sheet3 = sheets.Add
Sheet3.name = "Sheet3"
This is more cleanup than re-setting
cheers,
Micéal