logic causing variable not to pass to function - excel

I am having an issue getting a variable set to pass to a function properly. The frustrating part is that until yesterday this code has been working properly for a good 4 months of weekly use.
Below is the code, removing the dims and parts of the code that will not help resolve this issue.
Sub Combined_15_and_45()
'Dim Iteration Variables
'Dim tracking variables
'Dim range variables
'Dim Invoice Value Variables
'Dim Email body variables
Set wb = ThisWorkbook
Set WithTerms = Sheet4
Set APEmail = Sheet7
With wb
With WithTerms
lrow = .Cells(Rows.Count, 5).End(xlUp).Row
elrow = Sheet7.Cells(Rows.Count, 1).End(xlUp).Row
CalcDate = .Cells(1, 3).Value
i = 1
'loop through looking for times when cell above is different *Store i Instance
'loop through looking for times when cell below is different *Store i as EndInstance
'Specifically searching for changes in account number
For i = 4 To lrow
h = i - 1
j = i + 1
Set rng = .Cells(i, 5)
Set RngUp = .Cells(h, 5)
Set RngDwn = .Cells(j, 5)
'this is where vendor account changes.
If rng.Value <> RngUp.Value Then
instance = i
End If
'Check if invoice for the line is extreme past due *Store i as MaxOvrDue
If .Cells(i, 10).Value <= .Range("C1").Value - 45 Then
MaxOvrDue = i
End If
'check if invoice for line is +15 day overdue, less than 45 * Store i as MidOvrDur
If .Cells(i, 10).Value <= .Range("C1").Value - 15 Then
If .Cells(i, 10).Value >= .Range("C1").Value - 44 Then
If MidOvrdue = 0 Then
MidOvrdue = i
End If
End If
End If
'Check if Invoice for line is 15+ days overdue (Minimum) *Store i as Ovrdue
If .Cells(i, 10).Value < .Range("C1").Value Then
If .Cells(i, 10).Value <= .Range("C1").Value - 14 Then
OvrDue = i
End If
End If
'figure values for the totals of each section
If rng.Value <> RngDwn.Value Then
EndInstance = i
TotalOverdue = Application.WorksheetFunction.SumIfs(.Range("K:K"), .Range("E:E"), .Cells(instance, 5), .Range("J:J"), "<" & (.Range("c1") - 15))
XtrmOverdue = Application.WorksheetFunction.SumIfs(.Range("K:K"), .Range("E:E"), .Cells(instance, 5), .Range("J:J"), "<" & .Range("C1") - 44)
MidTotalOverdue = Application.WorksheetFunction.SumIfs(.Range("K:K"), .Range("E:E"), .Cells(instance, 5), .Range("J:J"), "<" & .Range("C1") - 15, .Range("J:J"), ">=" & .Range("C1") - 45)
If OvrDue = 0 And MaxOvrDue = 0 And MidOvrdue = 0 Then
Else:
'begin building Extremely Overdue Invoice Text
If MaxOvrDue <> 0 And MidOvrdue = 0 Then
**Set XtrmTblRng = .Range(.Cells(instance, 7), .Cells(MaxOvrDue, 11))**
End If
If OvrDue <> 0 And MidOvrdue <> 0 Then
If MaxOvrDue = 0 And OvrDue <= MidOvrdue Then
**Set MidTblRng = .Range(.Cells(MidOvrdue, 7), .Cells(OvrDue, 11))**
'Begin building ONLY overdue email text
Else:
'begin building segments to add to extreme overdue email
**Set XtrmComboTblRng = .Range(.Cells(instance, 7), .Cells(OvrDue, 11))**
End If
End If
If OvrDue <> 0 Then
'Generate the email
With OutMail
.To = eAddy
'Figure out which email to send
If MaxOvrDue <> 0 And MidTotalOverdue <> 0 Then
.HTMLbody = StrBodyXtrm & RangetoHTML(XtrmComboTblRng, CalcDate) & ComboStrBody2 & StrBody4
Else
If MaxOvrDue <> 0 And MidOvrdue = 0 Then
.HTMLbody = StrBodyXtrm & RangetoHTML(XtrmTblRng, CalcDate) & StrBody2 & StrBody4
Else:
.HTMLbody = StrBodyOverdue & RangetoHTML(MidTblRng, CalcDate) & StrBody3
End If
End If
.display
End With
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
End If
End If
'clear variables when changing vendor IDs
End If
Set rng = .Cells(j, 5)
Next i
End With
End With
End Sub
Function RangetoHTML(TblRng, CalcDate)
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
Dim tRow As Long
Dim i As Long
Dim CalcDate2 As Double
Dim TempDate As Double
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to paste the data in. This is where I am getting errors all the sudden
**TblRng.Copy**
'manipulate the data from table to fit needs and past into email.
End Function
Essentially this is trying to sort through stack of invoices and determine which email format to use.
Invoices aged 45+
Invoices aged 15-45+
Invoices aged 15-44
I am running into an issue with the email determining a different email format should be used than the TblRng variable that has been built to pass to the function. I just cant seem to find my logic error.
I have been banging my head against a wall for a few days now trying to fix this with no luck. Any help you can give will make you a hero in my eyes!
Thank you

The problem with your logic is with this line
If maxOvrDue = 0 And OvrDue <= midOvrDue Then
If there are no >45 lines (maxOvrDue = 0) then at the first occurance of a >15 line
midOvrDue and OvrDue will be the same. On subsequent >15 lines OvrDue will
be greater than midOvrDue. So the above will be true for 1 and false for 2 or more
lines in the 44-15 range. With 2 or more the default Else option will then Set XtrmComboTblRng not MidTblRng.
Later because maxOvrDue = 0 the email .HTMLbody uses RangetoHTML(MidTblRng, CalcDate).
The remedy would be to just use If maxOvrDue = 0 Then.
You could set an email type within the same logic as used to set the ranges so the mismatch can't occur. Here is an example of how to do that
Option Explicit
Sub Combined_15_and_45()
Dim WithTerms As Worksheet, APEMail As Worksheet
Dim rng As Range, tblRng As Range
Dim lrow As Long, elrow As Long, i As Long
Dim instance As Long, maxOvrDue As Long, midOvrDue As Long
Dim CalcDate As Date, DaysLate As Integer, EmailFormat As Integer
Dim has45 As Boolean, has15 As Boolean
Dim acc As String
Dim TotalOverdue As Currency
Dim XtrmOverdue As Currency, MidTotalOverdue As Currency
Set APEMail = Sheet7
elrow = APEMail.Cells(Rows.Count, 1).End(xlUp).row
' for debugging
Dim fso, ts
Set fso = CreateObject("Scripting.FileSystemObject")
' scan down sheet
Set WithTerms = Sheet4
With WithTerms
lrow = .Cells(Rows.Count, "E").End(xlUp).row 'Row E
CalcDate = .Range("C1").Value
For i = 4 To lrow
'this is where vendor account changes.
Set rng = .Cells(i, 5) ' E Account
If rng.Value <> rng.Offset(-1).Value Then
acc = rng
instance = i
maxOvrDue = 0
midOvrDue = 0
XtrmOverdue = 0
MidTotalOverdue = 0
End If
' check days overdue
DaysLate = DateDiff("d", .Cells(i, "J").Value, CalcDate)
If DaysLate >= 45 Then
maxOvrDue = i
XtrmOverdue = XtrmOverdue + .Cells(i, "K")
ElseIf DaysLate >= 15 Then
midOvrDue = i
MidTotalOverdue = MidTotalOverdue + .Cells(i, "K")
End If
' is this last for account
If rng <> rng.Offset(1) Then
TotalOverdue = XtrmOverdue + MidTotalOverdue
Debug.Print vbCr & acc & " Total", XtrmOverdue, MidTotalOverdue, TotalOverdue
has45 = maxOvrDue > 0
has15 = midOvrDue > 0
If has45 Or has15 Then
If has45 And has15 Then
EmailFormat = 1
Set tblRng = .Range(.Cells(instance, 7), .Cells(midOvrDue, 11))
Debug.Print "+45 and +15", tblRng.Address
' begin building segments to add to extreme overdue email
ElseIf has45 Then
EmailFormat = 2
Set tblRng = .Range(.Cells(instance, 7), .Cells(maxOvrDue, 11))
Debug.Print "+45 only", tblRng.Address
' begin building Extremely Overdue Invoice Text
ElseIf has15 Then
EmailFormat = 3
Set tblRng = .Range(.Cells(instance, 7), .Cells(midOvrDue, 11))
Debug.Print "+15 only", tblRng.Address
' begin building ONLY overdue email text
End If
' select email format
Dim body As String
Select Case EmailFormat
Case 1
body = "strBodyXtrm" & RangetoHTML(tblRng, CalcDate) & "ComboStrBody2 & strBody4"
Case 2
body = "strBodyXtrm" & RangetoHTML(tblRng, CalcDate) & "strBody2 & strBody4"
Case 3
body = "strBodyOverdue" & RangetoHTML(tblRng, CalcDate) & "strBody3"
End Select
' create html file for checking
Set ts = fso.createTextFile(ThisWorkbook.Path & "\" & acc & ".html", 1)
ts.write body
ts.Close
'Generate the email
'With outmail
'.To = eAddy
'.HTMLbody = body
'.display
'End with
End If
End If
Next i
End With
MsgBox "Done"
End Sub
Function RangetoHTML(tblRng, CalcDate) As String
Dim s, rw As Range, cell As Range, pre As String
pre = "<pre>TblRng=" & tblRng.Address(External:=1) & "</pre>"
s = "<tr align=""center"" bgcolor=""#ddddff"">" & _
"<th>Col G</th><th>Col H</th><th>Col I</th>" & _
"<th>Col J</th><th>Col K</th></tr>" & vbCrLf
For Each rw In tblRng.Rows
s = s & "<tr>"
For Each cell In rw.Cells
s = s & "<td>" & cell & "</td>"
Next
s = s & "</tr>" & vbCrLf
Next
RangetoHTML = pre & "<table cellspacing=""0"" cellpadding=""3"" border=""1"">" & _
s & "</table>" & vbCrLf
End Function

Related

VBA fetching emails from outlook too slow

So apparently here this macro fetches specific email addresses from inbox as well as sent items along with email addresses from cc,bcc
the problem is it takes a whole lot of time and i mean if a person has 2k emails he might have to wait for 3 to 4 hours .
Check some sources how to make code faster i got to know about restrict function when applied through DASL filter and limit number of items in a loop. i applied the same but the result is still the same and fetching is still slow .
As new into VBA i dont know all about optimization and still learning.
Any other sources or ways to make the fetching and execution faster ?
code given for reference
Option Explicit
Sub GetInboxItems()
'all vars declared
Dim ol As Outlook.Application
Dim ns As Outlook.Namespace
Dim fol As Outlook.Folder
Dim i As Object
Dim mi As Outlook.MailItem
Dim n As Long
Dim seemail As String
Dim seAddress As String
Dim varSenders As Variant
'for sent mails
Dim a As Integer
Dim b As Integer
Dim objitem As Object
Dim take As Outlook.Folder
Dim xi As Outlook.MailItem
Dim asd As String
Dim arr As Variant
Dim K As Long
Dim j As Long
Dim vcc As Variant
Dim seemail2 As String
Dim seAddress2 As String
Dim varSenders2 As Variant
Dim strFilter As String
Dim strFilter2 As String
'screen wont refresh untill this is turned true
Application.ScreenUpdating = False
'now assigning the variables and objects of outlook into this
Set ol = New Outlook.Application
Set ns = ol.GetNamespace("MAPI")
Set fol = ns.GetDefaultFolder(olFolderInbox)
Set take = ns.GetDefaultFolder(olFolderSentMail)
Range("A3", Range("A3").End(xlDown).End(xlToRight)).Clear
n = 2
strFilter = "#SQL=" & Chr(34) & "urn:schemas:httpmail:fromemail" & Chr(34) & " like '%" & seemail & "'"
strFilter2 = "#SQL=" & Chr(34) & "urn:schemas:httpmail:sentitems" & Chr(34) & " like '%" & seemail2 & "'"
'this one is for sent items folder where it fetches the emails from particular people
For Each objitem In take.Items.Restrict(strFilter2)
If objitem.Class = olMail Then
Set xi = objitem
n = n + 1
seemail2 = Worksheets("Inbox").Range("D1")
varSenders2 = Split(seemail2, ";")
For K = 0 To UBound(varSenders2)
'this is the same logic as the inbox one where if mail is found and if the mail is of similar kind then and only it will return the same
If xi.SenderEmailType = "EX" Then
seAddress2 = xi.Sender.GetExchangeUser.PrimarySmtpAddress
If InStr(1, seAddress2, varSenders2(K), vbTextCompare) Then
Cells(n, 1).Value = xi.Sender.GetExchangeUser().PrimarySmtpAddress
Cells(n, 2).Value = xi.SenderName
ActiveSheet.UsedRange.RemoveDuplicates Columns:=Array(1, 2), Header:=xlYes
On Error Resume Next
Range("A3:A9999").Select
Selection.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End If
'this is the smpt address (regular address)
ElseIf xi.SenderEmailType = "SMTP" Then
seAddress2 = xi.SenderEmailAddress
If InStr(1, seAddress2, varSenders2(K), vbTextCompare) Then
Cells(n, 1).Value = xi.SenderEmailAddress
Cells(n, 2).Value = xi.SenderName
ActiveSheet.UsedRange.RemoveDuplicates Columns:=Array(1, 2), Header:=xlYes
On Error Resume Next
Range("A3:A9999").Select
Selection.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End If
'this one fetches the cc part recipient denotes cc
For j = xi.Recipients.Count To 1 Step -1
If (xi.Recipients.Item(j).AddressEntry.Type = "EX") Then
vcc = xi.Recipients.Item(j).Address
If InStr(1, vcc, varSenders2(K), vbTextCompare) Then
Cells(n, 1).Value = xi.Recipients.Item(j).AddressEntry.GetExchangeUser.PrimarySmtpAddress
Cells(n, 2).Value = xi.Recipients.Item(j).Name
ActiveSheet.UsedRange.RemoveDuplicates Columns:=Array(1, 2), Header:=xlYes
On Error Resume Next
Range("A3:A9999").Select
Selection.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End If
Else
vcc = xi.Recipients.Item(j).Address
If InStr(1, vcc, varSenders2(K), vbTextCompare) Then
Cells(n, 1).Value = xi.Recipients.Item(j).Address
Cells(n, 2).Value = xi.Recipients.Item(j).Name
ActiveSheet.UsedRange.RemoveDuplicates Columns:=Array(1, 2), Header:=xlYes
On Error Resume Next
Range("A3:A9999").Select
Selection.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End If
End If
Next j
Else: seAddress2 = ""
End If
For a = 1 To take.Items.Count
n = 3
'this also fetches the recipient emails
If TypeName(take.Items(a)) = "MailItem" Then
For b = 1 To take.Items.Item(a).Recipients.Count
asd = take.Items.Item(a).Recipients(b).Address
If InStr(1, asd, varSenders2(K), vbTextCompare) Then
Cells(n, 1).Value = asd
Cells(n, 2).Value = take.Items.Item(a).Recipients(b).Name
n = n + 1
End If
Next b
End If
Next a
Next K
End If
Next objitem
For Each i In fol.Items.Restrict(strFilter)
If i.Class = olMail Then
Set mi = i
'objects have been assigned and can be used to fetch emails
seemail = Worksheets("Inbox").Range("D1")
varSenders = Split(seemail, ";")
n = n + 1
For K = 0 To UBound(varSenders)
'similar logic as above
If mi.SenderEmailType = "EX" Then
seAddress = mi.Sender.GetExchangeUser().PrimarySmtpAddress
If InStr(1, seAddress, varSenders(K), vbTextCompare) Then
Cells(n, 1).Value = mi.Sender.GetExchangeUser().PrimarySmtpAddress
Cells(n, 2).Value = mi.SenderName
ActiveSheet.UsedRange.RemoveDuplicates Columns:=Array(1, 2), Header:=xlYes
On Error Resume Next
Range("A3:A9999").Select
Selection.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End If
ElseIf mi.SenderEmailType = "SMTP" Then
seAddress = mi.SenderEmailAddress
If InStr(1, seAddress, varSenders(K), vbTextCompare) Then
Cells(n, 1).Value = mi.SenderEmailAddress
Cells(n, 2).Value = mi.SenderName
ActiveSheet.UsedRange.RemoveDuplicates Columns:=Array(1, 2), Header:=xlYes
On Error Resume Next
Range("A3:A9999").Select
Selection.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End If
For j = mi.Recipients.Count To 1 Step -1
If (mi.Recipients.Item(j).AddressEntry.Type = "EX") Then
vcc = mi.Recipients.Item(j).Address
If InStr(1, vcc, varSenders(K), vbTextCompare) Then
Cells(n, 1).Value = mi.Recipients.Item(j).AddressEntry.GetExchangeUser.PrimarySmtpAddress
Cells(n, 2).Value = mi.Recipients.Item(j).Name
ActiveSheet.UsedRange.RemoveDuplicates Columns:=Array(1, 2), Header:=xlYes
On Error Resume Next
Range("A3:A9999").Select
Selection.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End If
Else
vcc = mi.Recipients.Item(j).Address
If InStr(1, vcc, varSenders(K), vbTextCompare) Then
Cells(n, 1).Value = mi.Recipients.Item(j).Address
Cells(n, 2).Value = mi.Recipients.Item(j).Name
ActiveSheet.UsedRange.RemoveDuplicates Columns:=Array(1, 2), Header:=xlYes
On Error Resume Next
Range("A3:A9999").Select
Selection.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End If
End If
Next j
Else: seAddress = ""
End If
Next K
End If
Next i
ActiveSheet.UsedRange.RemoveDuplicates Columns:=Array(1, 2), Header:=xlYes
On Error Resume Next
Range("A3:A9999").Select
Selection.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
Set take = Nothing
Set mi = Nothing
Application.ScreenUpdating = True
End Sub
You have to assign a value to seemail and seemail2 before using in strFilter and strFilter2.
Option Explicit
Sub GetInbox_And_SentItems()
'Early binding - requires reference to Microsoft Outlook XX.X Object Library
Dim ol As Outlook.Application
Dim ns As Outlook.Namespace
Dim fol As Outlook.Folder
Dim folItem As Object
Dim mi As Outlook.mailItem
Dim n As Long
Dim seemail As String
Dim seAddress As String
Dim varSenders As Variant
'for sent mails
Dim b As Integer
Dim objitem As Object
Dim take As Outlook.Folder
Dim xi As Outlook.mailItem
Dim k As Long
Dim seemail2 As String
Dim seAddress2 As String
'Dim varSenders2 As Variant
Dim varReceivers As Variant
Dim strFilter As String
Dim strFilter2 As String
'screen won't refresh until this is turned true
'Application.ScreenUpdating = False
'now assigning the variables and objects of outlook into this
Set ol = New Outlook.Application
Set ns = ol.GetNamespace("MAPI")
Set fol = ns.GetDefaultFolder(olFolderInbox)
Set take = ns.GetDefaultFolder(olFolderSentMail)
'Range("A3", Range("A3").End(xlDown).End(xlToRight)).Clear
Range("A3:A9999").Select
Selection.EntireRow.Delete
n = 2
varReceivers = Split(Worksheets("Inbox").Range("D1"), ";")
For k = LBound(varReceivers) To UBound(varReceivers)
seemail2 = Trim(varReceivers(k))
Debug.Print seemail2
' Note displayto not fromemail
' displayto can be a difficult value
' https://stackoverflow.com/questions/16286694/using-the-restrict-method-in-outlook-vba-to-filter-on-single-recipient-email-ad
' As far as I know there is no working toemail.
strFilter2 = "#SQL=" & Chr(34) & "urn:schemas:httpmail:displayto" & Chr(34) & " like '%" & seemail2 & "'"
Debug.Print strFilter2
Debug.Print "Items in Inbox.........:" & take.Items.Count
Debug.Print "Filtered Items in Inbox:" & take.Items.Restrict(strFilter2).Count
'this one is for sent items folder where it fetches the emails --> to <-- particular people
' there is no point searching a sent folder for sender information
For Each objitem In take.Items.Restrict(strFilter2)
If objitem.Class = olMail Then
Set xi = objitem
n = n + 1
Cells(n, 1).Value = seemail2
Cells(n, 2).Value = xi.Subject
Dim msg As String
msg = ""
For b = 1 To xi.Recipients.Count
msg = msg & xi.Recipients(b).Address & "; "
Next b
Cells(n, 3).Value = msg
End If
Next objitem
Next k
varSenders = Split(Worksheets("Inbox").Range("D1"), ";")
For k = LBound(varSenders) To UBound(varSenders)
seemail = Trim(varSenders(k))
Debug.Print seemail
strFilter = "#SQL=" & Chr(34) & "urn:schemas:httpmail:fromemail" & Chr(34) & " like '%" & seemail & "'"
Debug.Print strFilter
For Each folItem In fol.Items.Restrict(strFilter)
If folItem.Class = olMail Then
Set mi = folItem
'objects have been assigned and can be used to fetch emails
n = n + 1
'similar logic as above
If mi.SenderEmailType = "EX" Then
seAddress = mi.Sender.GetExchangeUser().PrimarySmtpAddress
Cells(n, 1).Value = mi.Sender.GetExchangeUser().PrimarySmtpAddress
Cells(n, 2).Value = mi.SenderName
ElseIf mi.SenderEmailType = "SMTP" Then
seAddress = mi.SenderEmailAddress
Cells(n, 1).Value = mi.SenderEmailAddress
Cells(n, 2).Value = mi.Subject
End If
End If
Next folItem
Next k
ActiveSheet.UsedRange.RemoveDuplicates Columns:=Array(1, 2), Header:=xlYes
'Uncomment if needed
'On Error Resume Next
Range("A3:A9999").Select
Selection.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
On Error GoTo 0
Application.ScreenUpdating = True
End Sub
All the code touching the email from the outer loop should be taken out of the inner loop. E.g. the line like
seAddress2 = xi.Sender.GetExchangeUser.PrimarySmtpAddress
has no business being in the inner loop.
I also wouldn't call RemoveDuplicates on each step of the loop.
Also, most likely the senders won't be unique - retrieve all the sender addresses (SenderEmailAddress) in a single using MAPIFolder.GetTable and build a dictionary of EX type addresses vs SMTP addresses (GetExchangeUser.PrimarySmtpAddress) to be calculated only once for each unique address instead of retrieving it over and over again.

How to show results on desired sheet?

The below code is not showing the results on the "All Stock Analysis" sheet.
I tried doing a test after the activation of each worksheet (Range("I1).Interior.Color = vbGreen) and cell I1 turns green on each of the desired worksheets. What other tests can I try? No error msg pops up.
Sub AllStocksAnalysisRefactored()
Dim startTime As Single
Dim endTime As Single
yearValue = InputBox("What year would you like to run the analysis on?")
startTime = Timer
'Format the output sheet on All Stocks Analysis worksheet
Worksheets("All Stock Analysis").Activate
Range("A1").Value = "All Stocks (" + yearValue + ")"
'Create a header row
Cells(3, 1).Value = "Ticker"
Cells(3, 2).Value = "Total Daily Volume"
Cells(3, 3).Value = "Return"
'Initialize array of all tickers
Dim tickers(12) As String
tickers(0) = "AY"
tickers(1) = "CSIQ"
tickers(2) = "DQ"
tickers(3) = "ENPH"
tickers(4) = "FSLR"
tickers(5) = "HASI"
tickers(6) = "JKS"
tickers(7) = "RUN"
tickers(8) = "SEDG"
tickers(9) = "SPWR"
tickers(10) = "TERP"
tickers(11) = "VSLR"
'Activate data worksheet
Worksheets(yearValue).Activate
'Get the number of rows to loop over
RowCount = Cells(Rows.Count, "A").End(xlUp).Row
'1a) Create a ticker Index
Dim tickerIndex As Single
tickerIndex = 0
'1b) Create three output arrays
Dim tickerVolumes(12) As LongLong
Dim tickerstartingPrices(12) As Single
Dim tickerendingPrices(12) As Single
''2a) Create a for loop to initialize the tickerVolumes to zero.
For i = 0 To 11
tickerVolumes(i) = 0
''2b) Loop over all the rows in the spreadsheet.
For j = 2 To RowCount
'3a) Increase volume for current ticker
tickerVolumes(tickerIndex) = tickerVolumes(tickerIndex) + Cells(j, 8).Value
'3b) Check if the current row is the first row with the selected tickerIndex.
'If Then
If Cells(j - 1, 1).Value <> tickers(tickerIndex) And _
Cells(j, 1).Value = tickers(tickerIndex) Then
tickerstartingPrices(tickerIndex) = Cells(j, 6).Value
'End If
End If
'3c) check if the current row is the last row with the selected ticker
'If the next row’s ticker doesn’t match, increase the tickerIndex.
'If Then
If Cells(j + 1, 1).Value <> tickers(tickerIndex) And _
Cells(j, 1).Value = tickers(tickerIndex) Then
tickerendingPrices(tickerIndex) = Cells(j, 6).Value
'3d Increase the tickerIndex.
tickerIndex = tickerIndex + 1
'End If
End If
Next j
Next i
'4) Loop through your arrays to output the Ticker, Total Daily Volume, and Return.
For i = 0 To 11
Worksheets("All Stock Analysis").Activate
Next i
'Formatting
Worksheets("All Stock Analysis").Activate
Range("A3:C3").Font.FontStyle = "Bold"
Range("A3:C3").Borders(xlEdgeBottom).LineStyle = xlContinuous
Range("B4:B15").NumberFormat = "#,##0"
Range("C4:C15").NumberFormat = "0.0%"
Columns("B").AutoFit
dataRowStart = 4
dataRowEnd = 15
For i = dataRowStart To dataRowEnd
If Cells(i, 3) > 0 Then
Cells(i, 3).Interior.Color = vbGreen
Else
Cells(i, 3).Interior.Color = vbRed
End If
Next i
endTime = Timer
MsgBox "This code ran in " & (endTime - startTime) & _
" seconds for the year " & (yearValue)
End Sub
Here is how "All Stock Analysis" sheet will look after running the code:
You only need to scan the data sheet once if you use a dictionary object to convert the ticker ID to an array index number.
Option Explicit
Sub AllStocksAnalysisRefactored()
Const SHT_NAME = "All Stock Analysis"
Dim wb As Workbook, ws As Worksheet, wsYr As Worksheet
Dim cell As Range, yr As String, iRow As Long, iLastRow As Long
Dim t As Single: t = Timer
' choose data worksheet
yr = InputBox("What year would you like to run the analysis on ? ", "Enter Year", Year(Date))
Set wb = ThisWorkbook
On Error Resume Next
Set wsYr = wb.Sheets(yr)
On Error GoTo 0
' check if exists
If wsYr Is Nothing Then
MsgBox "Sheet '" & yr & "' does not exists.", vbCritical, "Error"
Exit Sub
End If
'Initialize array of all tickers
Dim tickerID, tickerData(), i As Integer, n As Integer
Dim dict As Object, sId As String
tickerID = Array("AY", "CSIQ", "DQ", "ENPH", "FSLR", "HASI", _
"JKS", "RUN", "SEDG", "SPWR", "TERP", "VSLR")
n = UBound(tickerID) + 1
ReDim tickerData(1 To n, 1 To 5)
' create dict id to index
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To n
sId = UCase(Trim(tickerID(i - 1)))
tickerData(i, 1) = sId ' id
tickerData(i, 2) = 0 ' volume
tickerData(i, 3) = 0 ' start price
tickerData(i, 4) = 0 ' finish price
tickerData(i, 5) = 0 ' count
dict.Add sId, i
Next
'Get the number of rows to loop over
iLastRow = wsYr.Cells(Rows.Count, "A").End(xlUp).Row
' Loop over all the rows in the spreadsheet.
' A=ticker, F=Price , H=Volume
For iRow = 2 To iLastRow
sId = UCase(Trim(wsYr.Cells(iRow, "A")))
If dict.exists(sId) Then
i = dict(sId)
' volume
tickerData(i, 2) = tickerData(i, 2) + wsYr.Cells(iRow, "H") ' volume
' start price when count is 0
If tickerData(i, 5) = 0 Then
tickerData(i, 3) = wsYr.Cells(iRow, "F")
End If
' end price
tickerData(i, 4) = wsYr.Cells(iRow, "F")
' count
tickerData(i, 5) = tickerData(i, 5) + 1
End If
Next
'Format the output sheet on All Stocks Analysis worksheet
Set ws = wb.Sheets(SHT_NAME)
ws.Cells.Clear
With ws
.Range("A1").Value2 = "All Stocks (" & yr & ")"
With .Range("A3:E3")
.Value2 = Array("Ticker", "Total Daily Volume", "Start Price", "End Price", "Return")
.Font.FontStyle = "Bold"
.Borders(xlEdgeBottom).LineStyle = xlContinuous
End With
.Range("A4").Resize(n, 4).Value2 = tickerData
.Range("B4:D4").Resize(n).NumberFormat = "#,##0"
.Range("E4").Resize(n).NumberFormat = "0.0%"
.Columns("B").AutoFit
End With
' coloring
For Each cell In ws.Range("E4").Resize(n)
cell.FormulaR1C1 = "=(RC[-1]-RC[-2])/RC[-2]" ' end-start/start
If cell > 0 Then
cell.Interior.Color = vbGreen
Else
cell.Interior.Color = vbRed
End If
Next
ws.Activate
ws.Range("A1").Select
MsgBox "This code ran for (" & yr & ")", vbInformation, Int(Timer - t) & " seconds"
End Sub

SAP fails on 2nd iteration of loop

Problem: The 2nd loop of For j fails on the line of code where I "Test for which line accrual is on 66-71"
Amt = session.FindById("wnd[0]/usr/lbl[" & CStr(g) & ",11]").Text
This was working a few weeks ago but my IT and our SAP consultant advise nothing has changed..
Error: Run-time error '619'. The control could not be found by id
Attempts: I have tried a few error trappings, included in my code below, but nothing works.
I have done a lot of research but come up blank. I've also re-recorded the macro and nothing has changed.. I'm lost and looking for help!
edit
Alternate Reslution: How can I get all items/amounts/string on this ROW
session.FindById("wnd[0]/usr/lbl[11]").Text - row 11
end edit
Code:
Option Explicit
Sub SAP_TPM_Payment()
'Declare Variables
Dim wkb As Workbook
Dim wks_TPM As Worksheet
Dim LR_TPM_A As Long, LR_TPM_E As Long, LR_DeletedSku As Long, AccAmt As Long, LR_Copy As Long, LR_clearing As Long, PayEntries As Long, PayCount As Long
Dim Response As VbMsgBoxResult, Response2 As VbMsgBoxResult
Dim WSHshell, proc
Dim wkb2 As String, wkb2_fname As String
Dim wkb2_name As String
Dim dblStartTime As Double 'time elapsed counter
Dim strMinutesElapsed As String
Dim i As Long, j As Long, k, n As Long, o As Long, p As Long, c As Long
Dim Amt, Amt1, Amt2, Amt3, Amt4, Amt5, Amt6
Dim LR_TPM_J As Long, Line, AccRow
Dim Status, sku, SAP_Acc, SAP_Pay, ClearNo, SAP_Accrual, PayAmt, Customer
Dim Pcheck1, Pcheck2
Dim CustomerName
Dim SapGuiAuto
Dim SAPApp As GuiApplication
Dim SAPCon As GuiConnection
Dim session As GuiSession
'Set Variables
Set wkb = ThisWorkbook
Set wks_TPM = wkb.Sheets("TPM Payment")
'Timer
dblStartTime = Timer
'Speed up code
NeedForSpeed
'Start Code
If wks_TPM.Range("Q2") = "" Then
MsgBox "No claim no. - exiting sub"
Exit Sub
End If
wks_TPM.Range("H" & Cells(Rows.Count, "I").End(xlUp).Row + 1) = Application.UserName
wks_TPM.Range("H" & Cells(Rows.Count, "I").End(xlUp).Row + 2) = Date
'Gets unique Accruals from col 'A', copies to col 'E'
If wks_TPM.Range("A4") = "" Then
wks_TPM.Range("E3:F3").Value = wks_TPM.Range("A3:B3").Value
Else:
LR_TPM_A = wks_TPM.Range("A" & Rows.Count).End(xlUp).Row
wks_TPM.Range("A2:A" & LR_TPM_A).AdvancedFilter Action:=xlFilterCopy, CopyToRange:=wks_TPM.Range("E2"), Unique:=True
End If
'Sums unique Accruals from col 'B', copies to col 'F'
LR_TPM_E = wks_TPM.Range("E" & Rows.Count).End(xlUp).Row
For i = 3 To LR_TPM_E
wks_TPM.Range("F" & i).Value = Application.SumIf(wks_TPM.Range("A:A"), wks_TPM.Range("E" & i), wks_TPM.Range("B:B"))
Next i
'Checks if SAP is open
On Error GoTo ErrRef
Response = MsgBox("Are you logged into SAP?" & vbCrLf & "" & vbCrLf & "Click 'Yes' if you are already logged into SAP" & vbCrLf & "Click 'No' to log into SAP" & vbCrLf & "Click 'Cancel' will exit the macro", vbYesNoCancel, "SAP Login Query")
If Response = vbNo Then
Set WSHshell = CreateObject("WScript.Shell")
Set proc = WSHshell.Exec("C:\Program Files (x86)\SAP\FrontEnd\SAPgui\saplogon.exe")
Response2 = MsgBox("Are you logged into SAP?" & vbCrLf & "" & vbCrLf & "Click 'Ok' once you have logged into SAP" & vbCrLf & "Click 'Cancel' will exit the macro", vbOKCancel, "SAP Login Query")
If Response2 = vbCancel Then
CreateObject("WScript.Shell").PopUp "Exiting macro...", 1, "SAP Login Query"
Exit Sub
End If
ElseIf Response = vbCancel Then
CreateObject("WScript.Shell").PopUp "Exiting macro...", 1, "SAP Login Query"
Exit Sub
End If
On Error GoTo 0
Set SapGuiAuto = GetObject("SAPGUI") 'Get the SAP GUI Scripting object
Set SAPApp = SapGuiAuto.GetScriptingEngine 'Get the currently running SAP GUI
Set SAPCon = SAPApp.Children(0) 'Get the first system that is currently connected
Set session = SAPCon.Children(0) 'Get the first session (window) on that connection
For j = 3 To LR_TPM_E
session.FindById("wnd[0]/tbar[0]/okcd").Text = "/n"
LR_TPM_J = wks_TPM.Range("J" & Rows.Count).End(xlUp).Row
SAP_Accrual = wks_TPM.Range("E" & j).Value
wks_TPM.Range("I" & LR_TPM_J + 1).Value = SAP_Accrual
session.FindById("wnd[0]").Maximize
session.FindById("wnd[0]/tbar[0]/okcd").Text = "/nVBO2"
session.FindById("wnd[0]").sendVKey 0
session.FindById("wnd[0]/usr/ctxtRV13A-KNUMA_BO").Text = SAP_Accrual
session.FindById("wnd[0]").sendVKey 0
'Confirms accrual is still active and not closed
Status = session.FindById("wnd[0]/usr/ctxtKONA-BOSTA").Text
If Status = "" Then
wks_TPM.Range("N" & LR_TPM_J + 1) = "Open"
Else
wks_TPM.Range("J" & LR_TPM_J + 1 & ":O" & LR_TPM_J + 1) = "Closed"
GoTo NextAccrual:
End If
'Confirms accrual is for the correct customer
Customer = session.FindById("wnd[0]/usr/txtKURGV-NAME1").Text
CustomerName = Split(Customer)(UBound(Split(Customer)))
If CustomerName = wks_TPM.Range("Q3") Then
wks_TPM.Range("Q4") = "Rebate Recipient matches claim"
Else
wks_TPM.Range("Q4") = "Rebate Recipient doesn't match claim"
GoTo NextAccrual:
End If
'Sales Volume (scrape accruals remaining)
session.FindById("wnd[0]").sendVKey 17
'Need to wait a second for SAP to catch up
Application.Wait (Now + TimeValue("00:00:01"))
Dim Cust As String
Cust = session.FindById("wnd[0]/usr/lbl[3,9]").Text
Dim CustPos As Long
CustPos = InStr(Cust, "a")
Debug.Print Cust
Debug.Print CustPos
''Test for which line amt is on
'Accrual sometimes on row 66-71, code for possibilities
Dim g
For g = 66 To 71
On Error GoTo Next_g:
Amt = session.FindById("wnd[0]/usr/lbl[" & CStr(g) & ",11]").Text
If IsEmpty(Amt) = False Then
If Amt <> "" Then
'Amt = session.FindById("wnd[0]/usr/lbl[" & CStr(g) & ",11]").Text
Debug.Print Amt & CStr(g)
'On Error GoTo 0
Exit For
End If
Else
Debug.Print CStr(g) & "nope"
End If
Next_g:
'On Error GoTo 0
Next g
' On Error Resume Next
' Amt1 = session.FindById("wnd[0]/usr/lbl[66,11]").Text
' If Err.Number <> 0 Then
' Err.Clear
' End If
' Amt2 = session.FindById("wnd[0]/usr/lbl[67,11]").Text
' Amt3 = session.FindById("wnd[0]/usr/lbl[68,11]").Text
' Amt4 = session.FindById("wnd[0]/usr/lbl[69,11]").Text
' Amt5 = session.FindById("wnd[0]/usr/lbl[70,11]").Text
' Amt6 = session.FindById("wnd[0]/usr/lbl[71,11]").Text
' On Error GoTo 0
'' 'Accrual sometimes on row 66-71, code for possibilities
'' On Error GoTo Handler1:
'' Amt1 = session.FindById("wnd[0]/usr/lbl[66,11]").Text
''Waypoint1:
'' On Error GoTo Handler2:
'' Amt2 = session.FindById("wnd[0]/usr/lbl[67,11]").Text
''Waypoint2:
'' On Error GoTo Handler3:
'' Amt3 = session.FindById("wnd[0]/usr/lbl[68,11]").Text
''Waypoint3:
'' On Error GoTo Handler4:
'' Amt4 = session.FindById("wnd[0]/usr/lbl[69,11]").Text
''Waypoint4:
'' On Error GoTo Handler5:
'' Amt5 = session.FindById("wnd[0]/usr/lbl[70,11]").Text
''Waypoint5:
'' On Error GoTo Handler6:
'' Amt6 = session.FindById("wnd[0]/usr/lbl[71,11]").Text
'' On Error GoTo 0
'AccAmt used for payment
AccAmt = LR_TPM_J + 1
''''''''''
'Amt Code'
''''''''''
'If IsEmpty(Amt) = False And Amt <> "" Then
Line = LR_TPM_J + 1
AccRow = -1
ReRun:
'********************
Dim ScrollBarPosOrig As Long, ScrollBarPosNew As Long, ScrollBarPosUpdate As Long
ScrollBarPosOrig = session.FindById("wnd[0]/usr").VerticalScrollbar.Position
session.FindById("wnd[0]/usr").VerticalScrollbar.Position = 2
ScrollBarPosUpdate = session.FindById("wnd[0]/usr").VerticalScrollbar.Position
If ScrollBarPosUpdate = ScrollBarPosOrig Then
GoTo ScrollBarNone:
Else
GoTo ScrollBar:
End If
ScrollBarNone:
For k = 10 To 100 Step 2 '1024 to act like infinity
sku = session.FindById("wnd[0]/usr/lbl[3," & CStr(k) & "]").Text
If sku = "" Then
MsgBox "Exit For"
End If
wks_TPM.Range("J" & Line) = Split(sku)(UBound(Split(sku))) 'gets last numbr from string, which is the sku/material
wks_TPM.Range("I" & Line) = SAP_Accrual
Amt = session.FindById("wnd[0]/usr/lbl[" & CStr(g) & "," & CStr(k + 1) & "]").Text
If Right(Amt, 1) = "-" Then 'Converts to number
wks_TPM.Range("K" & Line).Value = Left(Amt, Len(Amt) - 1)
Else: wks_TPM.Range("K" & Line).Value = "0" 'Zero $$ is the amount is a debit (overpaid accrual)
End If
Line = Line + 1
AccRow = AccRow + 1
Next k
ScrollBar:
session.FindById("wnd[0]/usr").VerticalScrollbar.Position = 0
ScrollBarPosOrig = session.FindById("wnd[0]/usr").VerticalScrollbar.Position
For k = 2 To 1000 Step 2 '1024 to act like infinity
sku = session.FindById("wnd[0]/usr/lbl[3,10]").Text
If sku = "" Then
MsgBox "Exit For"
End If
wks_TPM.Range("J" & Line) = Split(sku)(UBound(Split(sku))) 'gets last numbr from string, which is the sku/material
wks_TPM.Range("I" & Line) = SAP_Accrual
Amt = session.FindById("wnd[0]/usr/lbl[" & CStr(g) & ",11]").Text
If Right(Amt, 1) = "-" Then 'Converts to number
wks_TPM.Range("K" & Line).Value = Left(Amt, Len(Amt) - 1)
Else: wks_TPM.Range("K" & Line).Value = "0" 'Zero $$ is the amount is a debit (overpaid accrual)
End If
Line = Line + 1
AccRow = AccRow + 1
On Error Resume Next
SAP_Acc = session.FindById("wnd[0]/usr/lbl[3,13]").Text
Debug.Print Split(SAP_Acc)(UBound(Split(SAP_Acc)))
On Error GoTo 0
Debug.Print SAP_Accrual 'testing
If Split(SAP_Acc)(UBound(Split(SAP_Acc))) = CStr(SAP_Accrual) Then
GoTo EndOfAccrual:
End If
session.FindById("wnd[0]/usr").VerticalScrollbar.Position = k
Next k
EndOfAccrual:
On Error GoTo 0
'Copy delete sku formula
wks_TPM.Range("O2").Copy
wks_TPM.Range("O" & LR_TPM_J + 1 & ":O" & Cells(Rows.Count, "I").End(xlUp).Row).PasteSpecial Paste:=xlPasteFormulas
'Only calculates changed/updated cells
Application.Calculate
'Format cells
wks_TPM.Range("O3:O" & Cells(Rows.Count, "I").End(xlUp).Row).HorizontalAlignment = xlCenter
'If any sku is "marked for deletion", change amount to 0
LR_DeletedSku = wks_TPM.Range("O" & Rows.Count).End(xlUp).Row
For n = LR_TPM_J + 1 To LR_DeletedSku
If wks_TPM.Range("O" & n) = "X" Then
wks_TPM.Range("K" & n) = "0"
End If
Next n
'Copy amount to be paid formula
wks_TPM.Range("M2").Copy
wks_TPM.Range("M" & LR_TPM_J + 1 & ":M" & Cells(Rows.Count, "J").End(xlUp).Row).PasteSpecial Paste:=xlPasteFormulas
'Only calculates changed/updated cells
Application.Calculate
'Format cells
wks_TPM.Range("M3:M" & Cells(Rows.Count, "J").End(xlUp).Row).NumberFormat = "#,##0.00_)"
wks_TPM.Range("M3:M" & Cells(Rows.Count, "J").End(xlUp).Row).HorizontalAlignment = xlRight
'Go back one screen (equivalent of "F3")
session.FindById("wnd[0]").sendVKey 3
'Payment of Accrual
PayAmt = wks_TPM.Range("M" & AccAmt).Value
PayEntries = Line - AccAmt
If PayAmt <> "No" Then 'Pay Claim
session.FindById("wnd[0]").sendVKey 24 'Pay (equivalent of "Shift+F12")
'Test if there is a scrollbar
ScrollBarPosOrig = session.FindById("wnd[0]/usr").VerticalScrollbar.Position
ScrollBarPosNew = ScrollBarPosOrig + (o + 1)
ScrollBarPosUpdate = session.FindById("wnd[0]/usr").VerticalScrollbar.Position
If ScrollBarPosUpdate = ScrollBarPosOrig Then
GoTo ScrollBarNonePay:
Else
GoTo ScrollBarPay:
End If
ScrollBarNonePay:
For o = 0 To PayEntries
PayAmt = wks_TPM.Range("M" & AccAmt).Value
session.FindById("wnd[0]/usr/sub:SAPMV13A:3007/txtKONPD-BZWRT[" & CStr(o) & ",48]").Text = PayAmt
session.FindById("wnd[0]").sendVKey 0
AccAmt = AccAmt + 1
PayEntries = PayEntries - 1
If PayEntries = 0 Then
GoTo SavePayment:
End If
Next o
ScrollBarPay:
For o = 0 To PayEntries
PayAmt = wks_TPM.Range("M" & AccAmt).Value
session.FindById("wnd[0]/usr/sub:SAPMV13A:3007/txtKONPD-BZWRT[0,48]").Text = PayAmt
session.FindById("wnd[0]").sendVKey 0
AccAmt = AccAmt + 1
PayEntries = PayEntries - 1
If PayEntries = 0 Then
GoTo SavePayment:
End If
session.FindById("wnd[0]/usr").VerticalScrollbar.Position = (o + 1)
Next o
Else: GoTo NextAccrual:
End If
SavePayment:
'Need to wait a second for SAP to catch up
Application.Wait (Now + TimeValue("00:00:01"))
'Saves payment
session.FindById("wnd[0]").sendVKey 11
session.FindById("wnd[0]").sendVKey 0
'Need to wait a second for SAP to catch up
Application.Wait (Now + TimeValue("00:00:02"))
'Press "Enter" to go back into agreement
session.FindById("wnd[0]").sendVKey 0
'Selects rebate paymnts -> rebate docs
session.FindById("wnd[0]/mbar/menu[3]/menu[3]").Select
'Selects partial settelment
session.FindById("wnd[1]").sendVKey 0
'Selects first line
session.FindById("wnd[2]/usr/cntlCUSTOM_CONTAINER/shellcont/shell").SelectItem "000000000001", "COL0"
session.FindById("wnd[2]/usr/cntlCUSTOM_CONTAINER/shellcont/shell").ClickLink "000000000001", "COL0"
'Need to wait a second for SAP to catch up
Application.Wait (Now + TimeValue("00:00:02"))
'Selects Sales Doc -> Change
session.FindById("wnd[0]/mbar/menu[0]/menu[1]").Select
'Need to wait a couple of seconds for SAP to save - needs 2 secs, tried 1 sec but fails
Application.Wait (Now + TimeValue("00:00:02"))
'Updated date to today
session.FindById("wnd[0]/usr/tabsTAXI_TABSTRIP_OVERVIEW/tabpT\02/ssubSUBSCREEN_BODY:SAPMV45A:4415/ctxtVBKD-FKDAT").Text = Format(Date, "dd.mm.yyyy")
'Claim no.
session.FindById("wnd[0]/usr/subSUBSCREEN_HEADER:SAPMV45A:4021/txtVBKD-BSTKD").Text = wks_TPM.Range("Q2")
session.FindById("wnd[0]").sendVKey 0
'Selects Sales Document -> Billing -> Save
session.FindById("wnd[0]/mbar/menu[0]/menu[8]").Select
'Get Clearing Doc no.
ClearNo = session.FindById("wnd[0]/sbar").Text
If Split(ClearNo, Chr$(32))(1) Like "*6*" Then
wks_TPM.Range("L" & AccAmt - 1) = Split(ClearNo, Chr$(32))(1)
End If
'Enter thru "warning"
session.FindById("wnd[0]").sendVKey 0
'Need to wait a second for SAP to catch up
Application.Wait (Now + TimeValue("00:00:02"))
'Get Clearing Doc no. take 2 as enter thru errors producs different status bar text
ClearNo = session.FindById("wnd[0]/sbar").Text
If wks_TPM.Range("L" & AccAmt - 1) = "" Then
If Split(ClearNo, Chr$(32))(1) Like "*6*" Then
wks_TPM.Range("L" & AccAmt - 1) = Split(ClearNo, Chr$(32))(1)
End If
End If
session.FindById("wnd[0]").sendVKey 11
'Need to wait a second for SAP to catch up
Application.Wait (Now + TimeValue("00:00:02"))
NextAccrual:
Next j
'Copy clearing doc no.
For c = 3 To LR_TPM_A
wks_TPM.Range("C" & c).Value = Application.SumIf(wks_TPM.Range("I:I"), wks_TPM.Range("A" & c), wks_TPM.Range("L:L"))
Next c
Dim pathTPM_temp As String
Dim fnameTPM_temp As String
'Enter VB05N trans to get payment details
session.FindById("wnd[0]/tbar[0]/okcd").Text = "/nVA05N"
session.FindById("wnd[0]").sendVKey 0
session.FindById("wnd[0]/usr/ctxtSKUNNR-LOW").Text = ""
session.FindById("wnd[0]/usr/txtPBSTKD").Text = wks_TPM.Range("Q2")
session.FindById("wnd[0]").sendVKey 8
'Export to 'local file'
session.FindById("wnd[0]").sendVKey 45
session.FindById("wnd[1]/usr/subSUBSCREEN_STEPLOOP:SAPLSPO5:0150/sub:SAPLSPO5:0150/radSPOPLI-SELFLAG[1,0]").Select
session.FindById("wnd[1]/tbar[0]/btn[0]").Press
pathTPM_temp = "C:\Users\adamsmit\Desktop\"
session.FindById("wnd[1]/usr/ctxtDY_PATH").Text = pathTPM_temp
fnameTPM_temp = "export.xls"
session.FindById("wnd[1]/usr/ctxtDY_FILENAME").Text = fnameTPM_temp
session.FindById("wnd[1]").sendVKey 11
'Need to wait a couple of seconds for SAP to save - needs 2 secs, tried 1 sec but fails
Application.Wait (Now + TimeValue("00:00:01"))
Dim wkbTPM_temp As Workbook
Dim wksTPM_temp As Worksheet
'Open "export" file and filter for current payments
Set wkbTPM_temp = Workbooks.Open(FileName:=pathTPM_temp & fnameTPM_temp)
Set wksTPM_temp = Workbooks("export.xls").Worksheets("export")
'Format file
With wksTPM_temp
.Rows("5:5").EntireRow.Delete
.Rows("1:3").EntireRow.Delete
.Columns("A:A").EntireColumn.Delete
.AutoFilterMode = False
With .Range("A1:O1")
.AutoFilter
.AutoFilter Field:=2, Criteria1:=Environ("UserName")
.AutoFilter Field:=4, Criteria1:=Format(Date, "dd.mm.yyyy")
End With
End With
Debug.Print pathTPM_temp & fnameTPM_temp
With wkbTPM_temp
.Sheets.Add(After:=.Sheets(.Sheets.Count)).Name = "copy"
End With
Dim wksTPM_copy As Worksheet
Dim LR_hidden As Long
Set wksTPM_copy = Workbooks("export.xls").Worksheets("copy")
wksTPM_temp.Range("A1:" & wksTPM_temp.Range("K1").End(xlDown).Address).Copy wksTPM_copy.Range("A1")
'Get LR for copy to claims file below
LR_Copy = wksTPM_copy.Range("A" & Rows.Count).End(xlUp).Row
'Find Open Claims file to paste data into
Dim wbcount2 As Long
wbcount2 = Workbooks.Count
For i = 1 To wbcount2
If Workbooks(i).Path & "\" & Workbooks(i).Name Like "*" & wks_TPM.Range("Q2") & "*" Then
wkb2 = Workbooks(i).Path & "\" & Workbooks(i).Name
wkb2_fname = Workbooks(i).Path & "\" & Workbooks(i).Name
wkb2_name = Workbooks(i).Name
Debug.Print wkb2_name
Exit For
End If
Next i
Dim wkbClaim As Workbook
Dim wksClaim_clearing As Worksheet
Set wksClaim_clearing = Workbooks(wkb2_name).Worksheets("Clearing")
LR_clearing = wksClaim_clearing.Range("A" & Rows.Count).End(xlUp).Row
wksTPM_copy.Range("A2:K" & LR_Copy).Copy wksClaim_clearing.Range("A" & LR_clearing + 1)
'Kill temp "export" workbook
wkbTPM_temp.Close SaveChanges:=False
strMinutesElapsed = Format((Timer - dblStartTime) / 86400, "hh:mm:ss") 'stops timer - Determine how many seconds code took to run
MsgBox "This code ran successfully in " & strMinutesElapsed, vbInformation 'Msg box for elapsed time & Claims consldaited
ResetSpeed
Exit Sub
ErrRef: MsgBox ("Liar!!!" & vbCrLf & "" & vbCrLf & "Adam - 1" & vbCrLf & "You - 0")
ResetSpeed
Exit Sub
''Handler1: 'jump done, error handling is now disabled
'' Resume Waypoint1
''Handler2: 'jump done, error handling is now disabled
'' Resume Waypoint2
''Handler3: 'jump done, error handling is now disabled
'' Resume Waypoint3
''Handler4: 'jump done, error handling is now disabled
'' Resume Waypoint4
''Handler5: 'jump done, error handling is now disabled
'' Resume Waypoint5
''Handler6: 'jump done, error handling is now disabled
'' Resume Next
End Sub
How does your program behave when you make these changes?
...
Dim g
For g = 66 To 71
On Error Resume Next
Amt = session.FindById("wnd[0]/usr/lbl[" & CStr(g) & ",11]").Text
If err.number = 0 Then
On Error Goto 0
If Amt <> "" Then
'Amt = session.FindById("wnd[0]/usr/lbl[" & CStr(g) & ",11]").Text
Debug.Print Amt & CStr(g)
Exit For
End If
Else
On Error Goto 0
Debug.Print CStr(g) & "nope"
End If
Next g
...
Regards, ScriptMan
I would make the following change first:
...
Next_g:
'On Error GoTo 0
Next g
On Error Goto 0
...
Then you can see where is the actual error.
Regards, ScriptMan

startRange.offset - object does not support this property or method

Receiving an error when I run the code below. This worked last month, just seemed to stop working since I performed an update on Octobers data.
The script should grab data from Derek_Calc, which is a list of all logins on a daily basis to an application on the server. This data is then compressed to highlight how many people are logging in per hour on any given day.
The following line is used to set the date information for where the data will be added to the table and the dates for which to check in the DEREK_Calcs:
Set tempRange = target1.Range("B1706:B1736")
Sub PopulateConcurrency() 'for re-populating specific dates for the 'DEREK_Concurrency_Logins' sheet
'UPDATE THE DATE RANGE below!
Dim thisBook As Workbook
Dim target1 As Worksheet
Dim target2 As Worksheet
Dim dbSheetNames(1 To 2) As String
Dim cell As Variant
Dim cell2 As Variant
Dim searchDate As String
Dim firstColDate As Boolean
Dim userIdLoginCount As Long
Dim startHour As String
Dim endHour As String
Dim startDateTime As Date
Dim endDateTime As Date
Dim startDateHour As Date
Dim endDateHour As Date
Dim hourCounter As Integer
Dim startRange As Range
Dim endRange As Range
Dim tempString As String
Dim counter As Long
Dim userIds() As Long
Dim uniqueIds As Collection, c
Dim targCellRange As Range
Dim tempRange As Range
Dim tempRange2 As Range
dbSheetNames(1) = "DEREK_Concurrency_Logins"
dbSheetNames(2) = "DEREK_Calcs"
Set thisBook = ThisWorkbook
Set target1 = thisBook.Sheets(dbSheetNames(1))
Set target2 = thisBook.Sheets(dbSheetNames(2))
'prepare variables
userIdLoginCount = 0
hourCounter = 0
'de-activate re-calculations for this sheet as these will be updated later
target1.EnableCalculation = False
target2.EnableCalculation = False
'stop screen refreshing
Application.ScreenUpdating = False
Set tempRange = target1.Range("B1706:B1736") 'UPDATE THE DATE RANGE FROM COLUMN B Of THE 'DEREK_Concurrency_Logins' sheet
For Each cell In tempRange 'loop through each date in the DEREK_Concurrency_User_Logins sheet
searchDate = cell.Value
searchDate = Format(searchDate, "dd/mm/yyyy")
firstColDate = True
hourCounter = 0
For hourCounter = 0 To 16 'loop to next hour time range
'get start hour and end hour
startHour = target1.Cells(2, (3 + hourCounter))
startHour = Format(startHour, "hh:mm")
endHour = target1.Cells(2, (4 + hourCounter))
endHour = Format(endHour, "hh:mm")
'prepare variables
Erase userIds
Set uniqueIds = Nothing
Set uniqueIds = New Collection
userIdLoginCount = 0
counter = 0
With target2
Set tempRange2 = target2.Range("DEREK_LoginDaily")
For Each cell2 In tempRange2 'loop through each cell2 In DEREK_LoginDaily
If (StrComp(searchDate, cell2.Value) = 0) Then 'check for date match
If firstColDate = False Then
Set startRange = cell2
Set endRange = cell2
'get start and end hours for the hour period
startDateTime = startRange.Offset(0, 7).Value
endDateTime = endRange.Offset(0, 8).Value
'get the login start and finish times
tempString = Day(startDateTime) & "/" & Month(startDateTime) & "/" & Year(startDateTime) & " " & Format(startHour, "hh:mm")
startDateHour = CDate(tempString)
tempString = Day(endDateTime) & "/" & Month(endDateTime) & "/" & Year(endDateTime) & " " & Format(endHour, "hh:mm")
endDateHour = CDate(tempString)
If startDateTime <= startDateHour And endDateTime >= endDateHour Then
Sheets(dbSheetNames(2)).Select
startRange.Offset(0, 10).Select
startRange.Offset(0, 10).Activate
ReDim Preserve userIds(counter)
If (startRange.Offset(0, 10).Length > 0) Then
If startRange.Offset(0, 6).Value = 1 Then
userIds(counter) = startRange.Offset(0, 10).Value
End If
End If
counter = counter + 1 'increment counter
End If 'end hour concurency check
Else 'if firstColDate is True
startHour = target1.Cells(2, 2) 'code for 7am - 8am, set startHour to 07:00
endHour = target1.Cells(2, 4) 'set endHour to 08:00
Set startRange = cell2
Set endRange = cell2
'get start and end hours for the hour period
startDateTime = startRange.Offset(0, 7).Value
endDateTime = endRange.Offset(0, 8).Value
'get the login start and finish times
tempString = Day(startDateTime) & "/" & Month(startDateTime) & "/" & Year(startDateTime) & " " & Format(startHour, "hh:mm")
startDateHour = CDate(tempString)
tempString = Day(endDateTime) & "/" & Month(endDateTime) & "/" & Year(endDateTime) & " " & Format(endHour, "hh:mm")
endDateHour = CDate(tempString)
If startDateTime <= startDateHour And endDateTime >= endDateHour Then
Sheets(dbSheetNames(2)).Select
'THIS IS WHERE THE ERROR IS :-(
startRange.Offset(0, 10).Select
startRange.Offset(0, 10).Activate
ReDim Preserve userIds(counter)
If (startRange.Offset(0, 10).Length > 0) Then
If startRange.Offset(0, 6).Value = 1 Then
userIds(counter) = startRange.Offset(0, 10).Value
End If
End If
counter = counter + 1 'increment counter
End If 'end hour concurency check
End If 'end if firstColDate
End If 'end if a date match
Next cell2 'loop through each cell2 In DEREK_LoginDaily
End With
'get unique values by putting array into a collection
On Error Resume Next
For Each c In userIds
If Not IsEmpty(c) Then
uniqueIds.Add Item:=c, Key:=CStr(c)
End If
Next c
'populate target cell
Set targCellRange = cell
targCellRange.Offset(0, (2 + hourCounter)) = (uniqueIds.count)
firstColDate = False
Next hourCounter 'loop to next hour time range
firstColDate = True
Next cell 'loop through each date in the DEREK_Concurrency_User_Logins sheet
MsgBox "Complete"
End Sub
Not sure how, but this line is where the issue is:
startRange.Offset(0, 10).Length > 0
For a Range option you cannot have a length. I received some help and changed the line to this:
Len(startRange.Offset(0, 10).Value)
This is now populating correctly. The entire scripts job is to take a worksheet of data including login dates and times, and then populate another table detailing how many users were in the system on an hourly basis.
Thank you for the help everyone!

Make a table from imported list in Excel

I get output from a program imported to Excel in the following format:
Item 1
1 10
2 10
3 20
5 20
8 30
13 30
Item 2
1 40
2 40
3 50
5 50
8 60
13 60
Item 3
1 50
2 50
3 40
5 40
8 30
13 30
Now, I want to create a table where the values for each item is placed next to each other as below:
Item 1 Item 2 Item 3
1 10 40 50
2 10 40 50
3 20 50 40
5 20 50 40
8 30 60 30
13 30 60 30
I can think of ways to do this using formulas with a combination of INDIRECT other functions, but I can see right away that it will be a huge pain. Is there a clever way of doing this?
My approach would be something like this:
=VLOOKUP($A6;indirect("A"&(6+G$5*$X$4):"D"&(30+G$5*$X$4));4;FALSE)
where my first lookup table is from A6:D30, the second from A32:D56. X4 contains the value 26 which is the number of rows for each Item, and G5:AA5 is 0, 1, 2 ....
I would place this besides the Item 1 list and drag it sideways and downwards. I think the procedure should work, but I get syntax error.
I don't have much experience writing VBA, but I'm capable of reading and understanding it.
UPDATE:
At Siddharth's request:
Can you check out this.
It assumes a fixed format as it is shown in your example.
It can be made dynamic, but then you need to customize the code.
Option Explicit
Sub test()
Dim oCollection As Collection
Dim oDict As Variant
Dim oItem As Object
Dim iCnt As Integer
Dim iCnt_B As Integer
Dim iCnt_items As Integer
Dim iCnt_records As Integer
Dim iID As Integer
Dim iValue As Integer
Dim strKey As Variant
'Nr of items
iCnt_items = 3
'Records per item
iCnt_records = 6
'This dictionary will store the items
Set oCollection = New Collection
'Store dictionaries in collection
For iCnt = 0 To iCnt_items - 1
Set oDict = CreateObject("Scripting.Dictionary")
For iCnt_B = 1 To iCnt_records
iID = ThisWorkbook.Sheets(1).Cells((iCnt * (iCnt_records) + (iCnt + 1) + iCnt_B), 1).Value
Debug.Print iID
iValue = ThisWorkbook.Sheets(1).Cells((iCnt * (iCnt_records) + (iCnt + 1) + iCnt_B), 2).Value
Debug.Print iValue
oDict.Add iID, iValue
Next iCnt_B
oCollection.Add oDict, "item " & iCnt
Next iCnt
'Write collection to sheet
iCnt = 0
For Each oItem In oCollection
iCnt = iCnt + 1
ThisWorkbook.Sheets(2).Cells(1, 1 + iCnt).Value = "item " & iCnt
iCnt_B = 0
For Each strKey In oItem.keys
iCnt_B = iCnt_B + 1
ThisWorkbook.Sheets(2).Cells(1 + iCnt_B, 1).Value = strKey
ThisWorkbook.Sheets(2).Cells(1 + iCnt_B, 1 + iCnt).Value = oItem(strKey)
Next
Next oItem
End Sub
Edit: sorry for interrupting the conversation -> I didn't follow up the comment section while programming.
Sidenote:
If the ranges you work with are dynamic, I would go with a dictionary.
The reason why I'm saying this is because the dictionary object uses indexing on its records.
The key - pair structure being: ID, value
allows you to directly access the values corresponding the given ID.
In your example you are working with a clear ID - value structure.
Using numeric id's would actually be the fastest.
Since I already worked on this... Here is another way..
Assumptions:
Data starts at row 5 in Sheet1
Output will be generated in Sheet2
Code:
The below code uses Collections and Formulas to achieve what you want.
Sub Sample()
Dim wsInput As Worksheet, wsOutput As Worksheet
Dim ColItems As New Collection, ColSubItems As New Collection
Dim lRow As Long, i As Long, N As Long
Dim itm
Set wsInput = ThisWorkbook.Sheets("Sheet1")
Set wsOutput = ThisWorkbook.Sheets("Sheet2")
With wsInput
lRow = .Range("B" & .Rows.Count).End(xlUp).Row
.Columns(1).Insert
.Range("A5:A" & lRow).Formula = "=IF(ISERROR(SEARCH(""Item"",B5,1)),A4,B5)"
For i = 5 To lRow
On Error Resume Next
If InStr(1, .Range("B" & i).Value, "item", vbTextCompare) Then
ColItems.Add .Range("B" & i).Value, CStr(.Range("B" & i).Value)
Else
ColSubItems.Add .Range("B" & i).Value, CStr(.Range("B" & i).Value)
End If
On Error GoTo 0
Next i
End With
With wsOutput
.Cells.ClearContents
N = 2
'~~> Create Header in Row 1
For Each itm In ColItems
.Cells(1, N).Value = itm
N = N + 1
Next
N = 2
'~~> Create headers in Col 1
For Each itm In ColSubItems
.Cells(N, 1).Value = itm
N = N + 1
Next
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
lcol = .Cells(1, .Columns.Count).End(xlToLeft).Column
j = 2
For i = 2 To lcol
.Range(.Cells(j, i), .Cells(lRow, i)).Formula = "=SUMIFS(" & _
wsInput.Name & _
"!C:C," & wsInput.Name & _
"!A:A," & .Name & _
"!$" & _
Split(.Cells(, i).Address, "$")(1) & _
"$1," & _
wsInput.Name & _
"!B:B," & _
.Name & _
"!A:A)"
Next i
.Rows("1:" & lRow).Value = .Rows("1:" & lRow).Value
End With
wsInput.Columns(1).Delete
End Sub
Screenshot:
This is what I have tried.
Sheet 1 contains the data. The result is generated in Sheet 2
Sub createTable()
Dim counter As Integer
Dim countRow As Integer
Dim flag As Boolean
Dim cellAddress As String
flag = True
countRow = 2
counter = 2
ThisWorkbook.Sheets("Sheet1").Activate
For Each cell In Range("a:a")
If counter = 2 Then
If InStr(1, cell.Value, "Item") Then
ThisWorkbook.Sheets("Sheet2").Activate
ActiveSheet.Cells(1, counter).Value = cell.Value
firstItem = cell.Value
counter = counter + 1
End If
Else
ThisWorkbook.Sheets("Sheet2").Activate
If InStr(1, cell.Value, "Item") Then
ThisWorkbook.Sheets("Sheet2").Activate
ActiveSheet.Cells(1, counter).Value = cell.Value
counter = counter + 1
flag = False
End If
If flag = True Then
Cells(cell.Row, cell.Column) = cell.Value
End If
End If
If cell.Value = vbNullString Then
Exit For
End If
Next cell
ThisWorkbook.Sheets("Sheet1").Activate
Application.CutCopyMode = False
Dim counteradd As Integer
counteradd = 2
For Each cell In Range("a:a")
v = cell.Value
If InStr(1, cell.Value, "Item") Then
If cell.Offset(1, 1).Select <> vbNullString Then
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets("Sheet2").Select
Cells(2, counteradd).Select
ActiveSheet.Paste
Application.CutCopyMode = False
counteradd = counteradd + 1
ThisWorkbook.Sheets("Sheet1").Activate
End If
End If
Next cell
End Sub

Resources