Skip rows with empty cells - excel

I'm working on automating orders over WhatsApp with an Excel Sheet.
My programming experience is bare bones, so I used tutorials and other stack overflow threads to get to solutions. I have something that works, but these scripts send the full lists even if the item quantity cell is empty, which doesn't work for me.
From my understanding I need a If Else statement to do this, but I dont know where to place it.
The goal is to if a cell in the column is empty that row is skipped. How can I do that?
The below is the script that opens the browser and sends the messages.
Sub WebWhatsApp()
Dim pop As Range
Dim BOT As New WebDriver
Dim KS As New Keys
Dim count_row As Integer
count_row = ActiveSheet.Cells(Rows.Count, "A").End(xlUp).Row
Dim rng As Range
Set rng = Sheets("Interface").Range(Cells(12, 1), Cells(count_row, 5))
Dim myString As String
myString = Rang2String(rng)
BOT.Start "chrome", "https://web.whatsapp.com/"
BOT.Get "/"
MsgBox _
"Please scan the QR code." & _
"After you are logged in, please confirm this message box by clicking 'ok'", vbOKOnly, "WhatsApp Bot"
searchtext = Sheets("Interface").Range(Cells(5, 8), Cells(5, 8))
textmessage = myString
BOT.FindElementByXPath("//*[#id='side']/div[1]/div/label/div/div[2]").Click
BOT.Wait (500)
BOT.SendKeys (searchtext)
BOT.Wait (500)
BOT.SendKeys (KS.Enter)
BOT.Wait (500)
BOT.SendKeys (textmessage)
BOT.Wait (1000)
BOT.SendKeys (KS.Enter)
MsgBox "Done."
End Sub
And this is the script that turns a range of the Excel sheet into a string that the main script sends out as text messages.
Function Rang2String(rng As Range) As String
Dim strng As String
Dim myRow As Range
Dim KS As New Keys
With rng
For Each myRow In .Rows
strng = strng & Join(Application.Transpose(Application.Transpose(myRow.Value)), " | ") & vbNewLine
Next
End With
Rang2String = Left(strng, Len(strng) - 1)
End Function
I realize that the answer could be very obvious but I cant seem to see a solution.
Thanks in advance.

Option Explicit
Function Rang2String(rng As Range) As String
Const COL_QU = "D" ' quantity column
Dim e As String, myrow As Range
With Application
For Each myrow In rng.Rows
If Len(myrow.Cells(1, COL_QU)) > 0 Then
Rang2String = Rang2String & e & Join(.Transpose(.Transpose(myrow)), " | ")
e = vbNewLine
End If
Next
End With
End Function

Related

Troubleshoot "Unable to get the Buttons property of the Worksheet class"?

I created a 7-sheet Excel spreadsheet as a test-help companion to a popular bridge book by Kantar called "Modern Bridge Defense". The spreadsheet has a fairly large number of form buttons that allow a user to show or hide test answers from each of the seven chapters in the book
I have vba code that shows or hides the answer text, depending on the caption ('show' or 'hide') of the associated button, and a 'show/hide all' button that will show or hide all answers associated with a particular section of that chapter's test.
I also have vba code that initializes all the buttons on all 7 sheets. When the user first opens the spreadsheet, he/she is asked if they are OK with hiding all the answers. If they agree, the initialization routine loops through all 7 sheets, and the code for each sheet loops through all the buttons on that sheet, hiding each answer that isn't already hidden.
All this works fine if I step through the buttons in debug mode, but fails with "Unable to get the Buttons property of the Worksheet class" at some point (not always the same point) when I try to run it full speed.
This behavior seems like it might be some sort of timing/race issue, but I'm having trouble imagining how that could be, as I don't think I'm really tasking my laptop (XPS15 7590 with 32GB Ram, 1TB SSD).
Here is my initialization routine and the function it calls to iterate through the sheet buttons:
Option Explicit
Private Sub Workbook_Open()
Debug.Print ("In Workbook_Open()")
Dim res As VbMsgBoxResult
res = MsgBox("Hide all answers?", vbYesNoCancel, "Kantar Test Initialization")
If res = vbYes Then
res = MsgBox("Caution! This action will hide all answers - Are you SURE you want to do this?", vbYesNoCancel, "Are you SURE?")
If res = vbYes Then
'OK, user is sure about doing this!
Dim sheet As Worksheet
For Each sheet In Application.Sheets
Debug.Print "Initializing worksheet " & sheet.Name
On Error Resume Next
InitializeAllButtons sheet
If Err <> 0 Then
Debug.Print "call to InitializeAllButtons)" & sheet.Name & " failed with " & Err.Description
End If
Next sheet
End If
End If
End Sub
and here's the function that actually 'clicks' the buttons
Sub InitializeAllButtons(sheet As Worksheet)
Dim btn As Excel.Button, addrstr, startcellstr, startrowstr, endrowstr, colstr As String
Dim pos As Integer
Dim startrow As Integer
Dim endrow As Integer
Dim col As Integer
Dim rownum As Integer
With sheet
For Each btn In .Buttons
Debug.Print (vbTab & btn.Name & ", " & btn.Caption)
'11/27/22 rev to set 'all' button captions to 'Hide All', click on all 'Hide' row buttons
If InStr(btn.Caption, "All") > 0 Then
btn.Caption = "Show All"
Debug.Print "btn " & btn.Name & " caption changed to Show All"
Else 'is normal row show/hide button
If btn.Caption = "Hide" Then
RowButtonClick (btn.Name)
End If
End If
Next
End With
End Sub
In response to a question, here is the 'RowButtonClick()' function
Function RowButtonClick(btn_name As String) As Integer
Dim btn As Excel.Button
Dim btn_text As String
Dim cellstr As String
Dim row, col As Integer
Dim textrange As Range
'for multiple row show/hide ops
Dim cellstrlen As Integer
Dim startrowstr, endrowstr, startcolstr As String
Dim startrow As Integer
Dim endrow As Integer
Dim charidx As Integer
'11/24/22 multiple row button names include '_'
charidx = InStr(btn_name, "_")
If charidx > 0 Then
'11/24/22 row addresses may be 1, 2, or 3 digits
GetStartEndRowCol btn_name, startrow, endrow, col
Debug.Print ("RBC just after GSER: start row = " & startrow & ", end row = " & endrow)
Else 'single row: endrow = startrow
cellstr = Mid(btn_name, 9)
startrow = Range(cellstr).row
endrow = startrow
col = Range(cellstr).Column
Debug.Print "RBC: Single row show/hide action"
End If
Set btn = Application.ActiveSheet.Buttons(btn_name)
btn_text = btn.Caption
With Application.ActiveSheet
If btn_text = "Show" Then
Application.ActiveSheet.Range(.Cells(startrow, col + 1), .Cells(endrow, col + 2)).NumberFormat = ""
btn.Caption = "Hide"
Else
Application.ActiveSheet.Range(.Cells(startrow, col + 1), .Cells(endrow, col + 2)).NumberFormat = "; ; ;"
btn.Caption = "Show"
End If
End With
RowButtonClick = endrow 'so calling fcn knows the next row to try
End Function

Unable to update hyperlink address

I'm not very familiar with VBA and am stuck on the last step of finishing my code! What this code does is scan every sheet in the workbook for cells with hyperlinks, and then it goes into the hyperlink and prepends a URL onto it. Everything is working - the last Debug.Print here is properly printing out the correct URL. But then it just hangs and gets stuck on "link.Address = currentAddress". No warning, no error message, it just hangs and does nothing until I press Enter, at which point it highlights that line yellow.
I'm at a loss for how currentAddress can print just fine, but can't be set to the link's address? And it only happens for certain links. Here is one that WORKS:
http://localhost:8000/link?owner=lencompass&name=Create%20a%20JIRA%20ticket%20here!&worksheet=test%20sheet&test=true&destination=https://jira01.corp.censored.com:8443/secure/CreateIssue.jspa%3Fpid=14071%26issuetype=1
Here is one that does NOT WORK:
http://localhost:8000/link?owner=lencompass&name=Core%20Dash%20-%20Performance%20by%20Recipient%20Company%20%26%20Function(Last%2012%20months)&worksheet=test%20sheet&test=true&destination=https://censored.corp.censored.com/accounts/1337/insights/880%3FmultiPeers=309694%2C1586%2C10667%2C1441%2C1009%2C1337%2C1035%2C1028%2C3185%2C1815218%2C96622
These links work when I use them in the browser so I know they are valid links.
Here is my entire VBA script:
Sub trackify_links()
Dim I As Integer
' Loop through each sheet in this workbook
For I = 1 To ActiveWorkbook.Worksheets.Count
' loop through each cell in this sheet
Dim rwIndex As Long
Dim colIndex As Long
Dim maxRow As Long
maxRow = Worksheets(I).Cells(Worksheets(I).Rows.Count, 4).End(xlUp).Row
Worksheets("For IAs").Range("E16") = "Looping over " & maxRow & " rows in sheet: " & Worksheets(I).Name
For rwIndex = 1 To maxRow
' only loop up to the max filled-in column on this row
Dim maxColumn As Long
maxColumn = Worksheets(I).Cells(rwIndex, Worksheets(I).Columns.Count).End(xlToLeft).Column
For colIndex = 1 To maxColumn
Dim linkIndex As Long
Dim link As Hyperlink
For linkIndex = 1 To Worksheets(I).Cells(rwIndex, colIndex).Hyperlinks.Count
Set link = Worksheets(I).Cells(rwIndex, colIndex).Hyperlinks(linkIndex)
' only trackify a link if it isn't already
If Left(link.Address, 30) <> "http://localhost:8000/link?" Then
' this is a QA check - i noticed people putting their local machine paths as links here which won't work for anyone else. Output a list of weird links as a warning
If Left(link.Address, 3) = "../" Or Left(link.Address, 2) = "./" Then
Worksheets("For IAs").Range("E19") = "The link in cell (" & Col_Letter(colIndex) & rwIndex & ") in worksheet " & Worksheets(I).Name & " looks like it's a local path. These links will not work and have not been trackified - consider changing them."
Else
Dim currentAddress As String
' in order for the tracking link to properly redirect, there needs to be an "http://" or "https://" protocol at the beginning
If LCase(Left(link.Address, 7)) <> "http://" And LCase(Left(link.Address, 8)) <> "https://" Then
currentAddress = "https://" & link.Address
Else
currentAddress = link.Address
End If
' replace special characters with hex code so the link is not incorrectly parsed
currentAddress = ConvertToHex(currentAddress)
Dim extraParameters As String
extraParameters = "owner=" & ConvertToHex("lencompass") ' indicate this link belongs to lencompass
extraParameters = extraParameters & "&name=" & ConvertToHex(link.TextToDisplay) ' set the name of this link to the excel link's text"
extraParameters = extraParameters & "&worksheet=" & ConvertToHex(Worksheets(I).Name) ' indicate where in the workbook this link was clicked from (if tab format stays the same it basically will tell what kind of person is clicking)
If Worksheets("For IAs").Range("E3") <> "No" Then _
extraParameters = extraParameters & "&test=true" ' indicate this is a testing link if appropriate
' here we wrap the cell's current link into the tracking link, and customize it with some info about where in the workbook this link was clicked
Debug.Print ("currentaddress: " & currentAddress)
currentAddress = "http://localhost:8000/link?" & extraParameters & "&destination=" & currentAddress
Debug.Print (currentAddress)
link.Address = currentAddress
End If
End If
Next linkIndex
Next colIndex
Next rwIndex
Next I
End Sub
Function Col_Letter(lngCol As Long) As String
Dim vArr
vArr = Split(Cells(1, lngCol).Address(True, False), "$")
Col_Letter = vArr(0)
End Function
Function ConvertToHex(str As String) As String
ConvertToHex = Replace(Replace(Replace(Replace(str, "?", "%3F"), "&", "%26"), " ", "%20"), """", "%22")
End Function

check in- check out sytem using a barcode scanner

I'm trying to create a check in/out system at a lab I work at. I'm not to experienced at using VBA. I was able to tinker with some formulas to get it to do what I wanted, but I wasn't fully successful in getting all the steps I wanted done.
So what I'm trying to do is check in samples using a barcode followed by a date in the cell right next to it.
I want this formula to apply to A2000 so I can check in multiple samples. I'm using an input box and I want this input box to be able to detect matched samples and place them in the checked out column C followed by a date in the cell right next to it.
I would appreciate any help you guys can give me.
Here's the code I am currently using.
Private Sub Worksheet_Activate()
Dim myValue As Variant
Dim code As Variant
Dim matchedCell As Variant
myValue = InputBox("Please scan a barcode")
Range("A2").Value = myValue
Set NextCell = Cells(Rows.Count, "A").End(xlUp)
If NextCell.Row > 1 Then NextCell = NextCell.Offset(1, 0)
Set matchedCell = Range("a2:a2000").Find(what:=code, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=True)
If myValue = True Then Paste ("C2;C2000")
If Not matchedCell Is Nothing Then matchedCell.Offset(-1, 1).Value = Now
End Sub
To add data safety, I would differentiate the Check-In and the Check-Out process.
I'm not sure how you get the Code from the scanner ? Copied to prompt automatically ?
Anyway, below is my solution:
1.Transform your table into an excel table (CTRL+T) and name it "STORE_RECORDS" as below:
2.Create a module and paste following code:
Option Explicit
Sub Check_In()
Dim Code As String: Code = InputBox("Please scan a barcode", "Scan procedure")
If Code = "" Then MsgBox ("No code scanned"): Exit Sub
Dim NbChIn As Integer: NbChIn = Application.CountIf(Range("STORE_RECORDS[CHECK-IN]"), Code)
Dim NbChOut As Integer: NbChOut = Application.CountIf(Range("STORE_RECORDS[CHECK-OUT]"), Code)
If NbChIn > NbChOut And NbChIn > 0 Then
MsgBox ("This item is already Checked-In" & Chr(10) & "Please check it out and retry"): Exit Sub
Else
Cells(Rows.Count, 1).End(xlUp).Offset(1, 0) = Code
Cells(Rows.Count, 1).End(xlUp).Offset(0, 1) = Now
End If
End Sub
Sub Check_Out()
Dim Code As String: Code = InputBox("Please scan a barcode", "Scan procedure")
If Code = "" Then MsgBox ("No code scanned"): Exit Sub
Dim NbChIn As Integer: NbChIn = Application.CountIf(Range("STORE_RECORDS[CHECK-IN]"), Code)
Dim NbChOut As Integer: NbChOut = Application.CountIf(Range("STORE_RECORDS[CHECK-OUT]"), Code)
If NbChIn = NbChOut And NbChIn > 0 Then
MsgBox ("This item is already Checked-Out" & Chr(10) & "Please check it in and retry"): Exit Sub
Else
If Range("STORE_RECORDS[CHECK-IN]").Find(Code, , , xlWhole, , xlPrevious) Is Nothing Then MsgBox ("No match, ask Carlos !"): Exit Sub
Range("STORE_RECORDS[CHECK-IN]").Find(Code, , ,xlWhole , , xlPrevious).Offset(0, 2) = Code
Range("STORE_RECORDS[CHECK-IN]").Find(Code, , ,xlWhole , , xlPrevious).Offset(0, 3) = Now
End If
End Sub
3.Link Check-In and Check-Out buttons to respective procedures and you should be good to go.

Detect the renaming or deletion of worksheets

Is there a way to detect when a user
renames, or
deletes a worksheet?
I want to run some code if one of these events happens.
what I have tried
My tool uses a lot of event handlers so one thing I thought of was looping through all the sheetnames during each Worksheet_Change, but I don't think that is the best approach.
This approach goes under the ThisWorkbook module.
Public shArray1 As Variant
Public shArray2 As Variant
Private Sub Workbook_SheetDeactivate(ByVal Sh As Object)
Dim lngCnt As Long
Dim strMsg As String
Dim strSht
Dim vErr
Dim strOut As String
'get all sheet names efficiently in a 1D array
ActiveWorkbook.Names.Add "shtNames", "=RIGHT(GET.WORKBOOK(1),LEN(GET.WORKBOOK(1))-FIND(""]"",GET.WORKBOOK(1)))"
shArray2 = Application.Transpose([INDEX(shtNames,)])
strSht = Application.Transpose(Application.Index(shArray2, , 1))
'exit here if first time code is run
If IsEmpty(shArray1) Then
shArray1 = shArray2
Exit Sub
End If
`check each sheet name still exists as is
For lngCnt = 1 To UBound(shArray1)
vErr = Application.Match(shArray1(lngCnt, 1), strSht, 0)
If IsError(vErr) Then
strOut = strOut & shArray1(lngCnt, 1) & vbNewLine
vErr = Empty
End If
Next
shArray1 = Application.Transpose([INDEX(shtNames,)])
If Len(strOut) > 0 Then MsgBox strOut, vbCritical, "These sheets are gone or renamed"
End Sub

How to reference Text to respective email address using VBA

so I have set up an emailing system in which emails are sent out to people that own a specific item that have a due date coming up. There are at least 1,000 items on my excel sheet and each item has a specific owner. However the owners are labeled using an ID. The ID refers to an email address in another sheet called "Permissions" . My email function works, however I am having trouble with my recepients. I am not able to match the ID on the sheet that has the items to the email address in the other sheet. I am fairly new to VBA so please excuse my code. I am still learning. Thank you!
The worksheet name "Register" is the worksheet with all of the items and due dates.
Code :
Option Explicit
Sub TestEmailer()
Dim Row As Long
Dim lstRow As Long
Dim Message As Variant
Dim Frequency As String 'Cal Frequency
Dim DueDate As Date 'Due Date for Calibration
Dim vbCrLf As String 'For HTML formatting
Dim registerkeynumber As String 'Register Key Number
Dim class As Variant 'Class
Dim owner As String ' Owner
Dim status As String 'Status
Dim ws As Worksheet
Dim toList As Variant
Dim Ebody As String
Dim esubject As String
Dim Filter As String
Dim LQAC As String
With Application
.ScreenUpdating = True
.EnableEvents = True
.DisplayAlerts = True
End With
Set ws = Sheets(1)
ws.Select
lstRow = WorksheetFunction.Max(2, ws.Cells(Rows.Count, Range("CalDueDate").Column).End(xlUp).Row)
For Row = 2 To lstRow
DueDate = CDate(Worksheets("Register").Cells(Row, Range("DueDate").Column).Value) 'DUE DATE
registerkeynumber = Worksheets("Register").Cells(Row, Range("RegisterKey").Column).Value
class = Worksheets("Register").Cells(Row, Range("Class").Column).Value
status = Worksheets("Register").Cells(Row, Range("Status").Column).Value
LQAC = Worksheets("Register").Cells(Row, Range("LQAC").Column).Value
Filter = Worksheets("Permissions").Cells(Row, Worksheets("Permissions").Range("MailFilter").Column).Value
If DueDate - Date <= 7 And class > 1 And status = "In Service" And DueDate <> "12:00:00 AM" Then
vbCrLf = "<br><br>"
'THIS IS WHERE I AM NOT SURE IF I AM REFERENCING CORRECTLY. I AM NOT SURE HOW TO REFERENCE THE ID FROM THE 'REGISTER' AND MATCH IT WITH THE EMAIL ADDRESS IN THE 'PERMISSIONS' WORKSHEET. AS OF NOW I AM ONLY REFERENCING THE EMAIL ADDRESS BUT THEY ARE NOT MATCHING UP.
toList = Worksheets("Permissions").Cells(Row, Worksheets("Permissions").Range("Email").Column).Value 'RECEPIENT OF EMIAL
esubject = "TEXT " & Cells(Row, Range("Equipment").Column).Value & " is due in the month of " & Format(DueDate, "mmmm-yyyy")
Ebody = "<HTML><BODY>"
Ebody = Ebody & "Dear " & Cells(Row, Range("LQAC").Column).Value & vbCrLf
Ebody = Ebody & "</BODY></HTML>"
SendEmail Bdy:=Ebody, Subjct:=esubject, Two:=toList
End If
Next Row
With Application
.ScreenUpdating = True
.EnableEvents = True
.DisplayAlerts = True
End With
End Sub
Function SendEmail(Bdy As Variant, Subjct As Variant, Optional Two As Variant = "Email#xxx", Optional ReplyTo As Variant = "Email#xxx", Optional Carbon As Variant = "Email#xxx", Optional Attch As Variant = "FilePath", Optional Review As Boolean = False)
Dim OutlookEM As Outlook.Application
Dim EMItem As MailItem
If Not EmailActive Then Exit Function
If Two = "Email#xxx" Then
MsgBox "There is no Address to send this Email"
Two = ""
Review = True
'Exit Function
End If
'Create Outlook object
Set OutlookEM = CreateObject("Outlook.Application")
'Create Mail Item
Set EMItem = OutlookEM.CreateItem(0)
With EMItem
.To = Two
.Subject = Subjct
.HTMLBody = Bdy
End With
If ReplyTo <> "Email#xxx" Then EMItem.ReplyRecipients.Add ReplyTo
If Attch <> "FilePath" Then EMItem.Attachments.Add Attch
If Carbon <> "Email#xxx" Then EMItem.CC = Carbon
If Review = True Then
EMItem.Display (True)
Else
EMItem.Display
' EMItem.Send
End If
End Function
I think I am able to follow what the issue is here. It doesn't look like your code is using any vlookup formula or matching formula to find the email. Unless they are on the same row between the different sheets, you will need to find the value.
VBA has the ability to use the functions that you would normally use in Excel.
If you tweek the code below with the correct range and column number, you should be able to find the correct email address based on an ID.
' instead of 1 below, use the column for the id to look up
lookupValue = Worksheets("Register").Cells(Row, 1).Value
' range of the ids and emails in the permissions table - edit whatever the range should be
Rng = Worksheets("Permissions").Range("A1:B100")
' column to look up - number of columns between the id and email in the permissions tab
col = 2
' whether you want excel to try to find like match for the lookup value
' pretty much never have this be true if you want to have confidence in the result
likeMatch = False
emailAddress = WorksheetFunction.VLookup(lookupValue, Rng, col, likeMatch)

Resources