Here is what I have been given to try and create:
User creates an email a few times per week and has to re-type everything, a request for employee updates, with up to 5 people on it. Easy enough to create in VBA, except that the employees could change each time. So there could be just 1 person, or 2, or 3, etc...and each time it could be a different mix of the employees. They want input boxes that would prompt how many employees for the email, then based on that input, follow-up boxes (if more than 1) that allow the input of the names (1 per box). It then needs to create the email, placing the input box data into the body text. Each email text will be based on the input from the 1st input box, so it can adjust for the number of employees (so there could be up to 5 employees on each email).
How do I assign values to my variables (findstrs and foundcells)so that they will adjust to the inputs of the inputboxes without writing all the IF stmts?
Dim aOutlook As Object
Dim aEmail As Object
Dim rngeAddresses As Range, rngeCell As Range, strRecipients As String
Dim strbox As String
Dim stritem As String
Dim X As Long
Dim r As Long
Dim LR, lookrng As Range
Dim findStr As String
Dim nameCol As Range
Dim nameCol1 As Range
Dim nameCol2 As Range
Dim nameCol3 As Range
Dim nameCol4 As Range
Dim foundCell As Variant
Dim foundCell1 As Variant
Dim foundcell2 As Variant
Dim strname As String
Dim strBody As String
Dim sigString As String
Dim signature As String
Dim findstr1 As String
Dim foundrng As Range
Dim valuefound As Boolean
Dim strFilename As String
Select Case Application.ActiveWindow.Class
Case olInspector
Set oMail = ActiveInspector.CurrentItem
Case olExplorer
Set oMail = ActiveExplorer.Selection.Item(1)
End Select
If Dir(sigString) <> "" Then
signature = GetBoiler(sigString)
Else
signature = ""
End If
Set oReply = oMail.ReplyAll
Select Case Application.ActiveWindow.Class
Case olInspector
Set oMail = ActiveInspector.CurrentItem
Case olExplorer
Set oMail = ActiveExplorer.Selection.Item(1)
End Select
Set aOutlook = CreateObject("Outlook.Application")
Set oReply = aOutlook.CreateItem(0)
'Input box(es)
findStr = InputBox("Enter Number of Employees")
findstr1 = InputBox("Enter Name of First Employee")
If findStr = "2" Then findstr2 = InputBox("Enter Name of Second Employee")
If findstr1 = "T1" Then foundCell1 = "<B>Test 1 ID#0000</B>"
If findstr1 = "T2" Then foundcell2 = "<B>Test 2 IDO#0001</B>"
If findstr1 = "T3" Then foundcell3 = "<B>Test 3 ID#0002</B>"
If findstr1 = "T4" Then foundCell4 = "<B>Test 4 ID#0003</B>"
If findstr1 = "T5" Then foundCell5 = "<B>Test 5 ID#0004</B>"
If findstr2 = "T1" Then foundCell1 = "<B>Test 1 ID#0000</B>"
If findstr2 = "T2" Then foundcell2 = "<B>Test 2 IDO#0001</B>"
If findstr2 = "T3" Then foundcell3 = "<B>Test 3 ID#0002</B>"
If findstr2 = "T4" Then foundCell4 = "<B>Test 4 ID#0003</B>"
If findstr2 = "T5" Then foundCell5 = "<B>Test 5 ID#0004</B>"
'Greeting based on time of day
Select Case Time
Case 0.25 To 0.5
GreetTime = "Good morning"
Case 0.5 To 0.71
GreetTime = "Good afternoon"
Case Else
GreetTime = "Good evening"
End Select
sigString = Environ("appdata") & _
"\Microsoft\Signatures\Update.htm"
If Dir(sigString) <> "" Then
signature = GetBoiler(sigString)
Else
signature = ""
End If
If findStr = "1" Then
strBody = "<Font Face=calibri>Can you please update the following: <br><br>" & _
"<B>" & foundCell1 & "</B><br><br>" & _
"Please update this batch. " & _
"I Appreciate your help. Let me know if you need anything.<br><br>" & _
"Thanks <br><br>" & _
subject = "Employee Update"
ElseIf findStr = "2" Then
strBody = "<Font Face=calibri>Can you please add changes for the following: " & _
"<ol><li><B>" & foundCell1 & "</B><br><br><br><br>" & _
"<li><B>" & foundcell2 & "</B><br><br>" & _
subject = "Multiple Employee Requests"
End If
'Sets up the email itself and then displays for review before sending
With oReply
.HTMLBody = "<Font Face=calibri>Hi there,<br><br>" & strBody & signature
.To = "superman#krypton.com"
.CC = "trobbins#shawshank.com "
.subject = "Multiple Employee Updates"
.Importance = 2
.Display
End With
End Sub
You need to break this code down into multiple, smaller and parameterized scopes.
Make a Function that returns the body of the email given a Collection of batch numbers.
Private Function GetEmailBody(ByVal batchNumbers As Collection) As String
Now, the calling code needs to know how many employees there are. Make a function for that.
Private Function GetNumberOfEmployees() As Long
Dim rawInput As Variant
rawInput = InputBox("Number of employees?")
If StrPtr(rawInput) = 0 Then
'user cancelled out of the prompt
GetNumberOfEmployees = -1
Exit Function
Else If IsNumeric(rawInput) Then
GetNumberOfEmployees = CLng(rawInput)
End If
End Function
That'll return -1 if user cancels the prompt, 0 for an invalid input, and the number of employees otherwise.
Dim employeeName As String
Dim nbEmployees As Long
nbEmployees = GetNumberOfEmployees
If nbEmployees = -1 Then
Exit Sub 'bail out
Else If nbEmployees = 0 Then
'reprompt?
Exit Sub 'bail out, cancelled
End If
'fun part here
Dim emailbody As String
emailBody = GetEmailBody(batchNumbers, employeeName)
And now the fun part: you need to add as many items to some batchNumbers collection, as you have nbEmployees. Because you know how many iterations you'll need before you start looping, a For loop will do.
Dim batchNumbers As Collection
Set batchNumbers = New Collection
Dim batchNumber As String
Dim i As Long
For i = 1 To nbEmployees
batchNumber = GetBatchNumber(i)
If batchNumber = vbNullString Then Exit Sub 'bail out:cancelled/invalid
batchNumbers.Add batchNumber
Next
Dim body As String
body = GetEmailBody(batchNumbers)
Where GetBatchNumber(i) is yet another function call, to a function whose role it is to prompt for an employee number and lookup & return the corresponding batch number, returning an empty string if prompt is cancelled or no match is found.
Private Function GetBatchNumber(ByVal index As Long) As String
Dim rawInput As Variant
rawInput = InputBox("Name of employe " & index & ":")
If StrPtr(rawInput) = 0 Then
'cancelled
Exit Function
Else
Dim employeeName as String
employeeName = CStr(rawInput)
GetBatchNumber = GetBatchForEmployee(employeeName)
End If
End Function
If the mappings really actually look like T1 -> <B>Test 1 ID#000</B> then you can probably use this:
Private Function GetBatchForEmployee(ByVal employeeName As String)
Dim digit As Long
digit = CLng(Right$(employeeName, 1))
GetBatchForEmployee = "<B>Test " & digit & " ID#" & Format$(digit - 1, "000") & "</B>"
End Function
If your mappings are actual mappings then you can have a Dictionary lookup in here, or look them up on an Excel worksheet, a CSV or XML data file, a SQL Server database, whatever.
But first, break things down. A procedure that starts like this:
Dim aOutlook As Object
Dim aEmail As Object
Dim rngeAddresses As Range, rngeCell As Range, strRecipients As String
Dim strbox As String
Dim stritem As String
Dim X As Long
Dim r As Long
Dim LR, lookrng As Range
Dim findStr As String
Dim nameCol As Range
Dim nameCol1 As Range
Dim nameCol2 As Range
Dim nameCol3 As Range
Dim nameCol4 As Range
Dim foundCell As Variant
Dim foundCell1 As Variant
Dim foundcell2 As Variant
Dim strname As String
Dim strBody As String
Dim sigString As String
Dim signature As String
Dim findstr1 As String
Dim foundrng As Range
Dim valuefound As Boolean
Dim strFilename As String
...is a procedure that's doing way too many things.
Related
I am trying to Vlookup a cell to return a match in a contact list.
When it finds that match it should send an email to the person associated with that location.
Sub vLookupAnotherWorksheet()
Dim myLookupValue As String
Dim myFirstColumn As Long
Dim myLastColumn As Long
Dim myColumnIndex As Long
Dim myFirstRow As Long
Dim myLastRow As Long
Dim myVLookupResult As Long
Dim myTableArray As Range
myLookupValue = "H3:H13"
myFirstColumn = 1
myLastColumn = 8
myColumnIndex = 8
myFirstRow = 3
myLastRow = 13
With Worksheets("EVC_Contact_List")
Set myTableArray = .Range(.Cells(myFirstRow, myFirstColumn), .Cells(myLastRow, myLastColumn))
End With
On Error Resume Next
myVLookupResult = WorksheetFunction.VLookup(myLookupValue, myTableArray, myColumnIndex, False)
If IsError(myVLookupResult) = False Then
Call Send_Email(myvalue)
End If
End Sub
Sub Send_Email(myvalue As Variant)
Dim Email_Subject As String, Email_Send_From As String, Email_Body As String, i As Integer
Dim Mail_Object As Object, nameList As String, namelist2 As String, o As Variant
Email_Send_From = ""
If Sheets("EVC_Contact_List").Cells(2, 4).Value <> "" Then
nameList = Join(Application.WorksheetFunction.Transpose(Sheets("EVC_Contact_List").Range("D2:D29")))
namelist2 = Join(Application.WorksheetFunction.Transpose(Sheets("EVC_Contact_List").Range("F2:F29")))
End If
Set Mail_Object = CreateObject("Outlook.Application")
With Mail_Object.CreateItem(o)
.Subject = "Unit(s) Excceding Days as Loaner"
.To = nameList
.Cc = namelist2
.display
End With
Application.DisplayAlerts = False
End Sub
If location XXXX is found on the contact list and Johnsmith#gmail.com is associated with that location it should send an email only to John Smith.
My code is sending an email to everyone on the contact list.
You have this:
nameList = Join(Application.WorksheetFunction.Transpose(Sheets("EVC_Contact_List").Range("D2:D29")))
Should that not be the Vlookup function?
I wrote a macro that works out quite well. I'm able to copy and paste given range (to be precise a pivot table) as bitmap but the problem is that not the whole are is copied, only a part of a table.
Here is the code, what's wrong with pasting? Why can't I copy the whole table?
Public Sub Lotus_Mail()
Dim NSession As Object
Dim NUIWorkSpace As Object
Dim NDatabase As Object
Dim NDoc As Object
Dim NUIdoc As Object
Dim Subject As String
Dim SendTo As String, CopyTo As String
Dim pivots As Range
Dim Month As String
Dim text1 As Range
Dim text2 As Range
Dim i As Integer
Dim arrHUBs(1 To 8) As String
arrHUBs(1) = "a"
arrHUBs(2) = "b"
arrHUBs(3) = "c"
arrHUBs(4) = "d"
arrHUBs(5) = "e"
arrHUBs(6) = "f"
arrHUBs(7) = "g"
arrHUBs(8) = "h"
Week = DatePart("ww", Date, vbMonday, vbFirstFourDays)
Month = MonthName(DatePart("m", Date), False)
On Error Resume Next
For x = 1 To 8
SendTo = Application.WorksheetFunction.VLookup(arrHUBs(x), Sheets("Mail").Range("A2:C9"), 2, 0)
CopyTo = Application.WorksheetFunction.VLookup(arrHUBs(x), Sheets("Mail").Range("A2:C9"), 3, 0)
Subject = "Summary " & arrHUBs(x) & " - " & Month & ": week " & Week
'area to select (pivot table)
rows = Sheets("sheet").Cells(Rows.Count, 21).End(xlUp).Row
columns = Sheets("sheet").Cells(6, Columns.Count).End(xlToLeft).Column
Set pivots = Sheets("sheet").Range(Cells(4, 19), Cells(wiersz, kolumna))
'Set pivots = Sheets("sheet").PivotTables("Pivot1") ???this line doesn't work, any other way to select pivot and paste to Lotus?
Set text1 = Sheets("Mail").Range("A12")
Set text2 = Sheets("Mail").Range("A13")
'Lotus step by step
Set NSession = CreateObject("Notes.NotesSession")
Set NUIWorkSpace = CreateObject("Notes.NotesUIWorkspace")
Set NDatabase = NSession.GetDatabase("", "")
If Not NDatabase.IsOpen Then NDatabase.OPENMAIL
'creating mail
Set NDoc = NDatabase.CreateDocument
With NDoc
.SendTo = SendTo
.CopyTo = CopyTo
.Subject = Subject
'Email body text, including a placeholder which will be replaced by Excel table
.body = text1 & vbLf & vbLf & _
"{IMAGE_PLACEHOLDER}" & vbLf
.Save True, False
End With
'Edit the new document using Notes UI to copy and paste pivot table into it
Set NUIdoc = NUIWorkSpace.EDITDocument(True, NDoc)
With NUIdoc
Sheets("sheet").Select
'Find the placeholder in the Body item
.GotoField ("Body")
.FINDSTRING "{IMAGE_PLACEHOLDER}"
'.DESELECTALL 'Uncomment to leave the placeholder in place (cells are inserted immediately before it)
'Copy pivot table (being a range) as a bitmap to the clipboard and paste into the email
pivots.CopyPicture xlBitmap
.Paste 'maybe any paste special option exists?
Application.CutCopyMode = False
'.Send
'.Close
End With
Set NSession = Nothing
Next x
End Sub
Thank you for your answers
Below is my attempt at adding yes/no options to my objshell.popup, getting a type mismatch error, probably doing something wrong...
got it from this website: http://www.informit.com/articles/article.aspx?p=1170490&seqNum=5
Public Sub ShowTable()
Dim myData
Dim myStr As String
Dim x As Integer
Dim myRange As Range
Dim lastrow As Long
Dim nsecond As Long
Dim ws As Worksheet
Call reviewME
UserForm1.Show
Set ws = Worksheets("New Lookups")
lastrow = ws.Cells(Rows.Count, 262).End(xlUp).Row
Set myRange = ws.Range(ws.Cells(2, 262), ws.Cells(lastrow, 262))
myData = myRange.Value
For x = 1 To UBound(myData, 1)
myStr = myStr & myData(x, 1) & vbTab & vbCrLf
Next x
'myStr = myStr & vbNewLine & "Proceed with change requests?"
inttype = vbYesNo + vbQuestion + vbDefaultButton2
Set objshell = CreateObject("Wscript.Shell")
strtitle = "Review your entries"
nsecond = 1
intresult = objshell.popup(myStr, nsecond, strtitle, inttype)
Select Case intresult
Case vbYes
MsgBox "hi"
Case vbNo
MsgBox "no"
End Select
It's because the signature for the Popup method is actually:
WshShell.Popup(strText, [nSecondsToWait], [strTitle], [intType])
and you are forgetting the nSecondsToWait parameter.
nSecondsToWait may be an optional param (as indicated by the brackets around the param name) but if you aren't going to include it then you need to leave an empty slot for it:
intresult = objshell.popup(myStr, , strtitle, inttype)
The type mismatch error is because the second param should be an integer (nSecondsToWait) but you are giving it a string ("Review your entries").
this is my code. I have column C that has duplicate names and column B that has unique IDs I need to find which unique IDs match with what names and send an email to the names and paste the unique IDs in the email. I am getting an error on the first Me.Cells.
Sub sendEmails()
Dim dict_emails As Scripting.dictionary
Set dict_emails = New Scripting.dictionary
Dim objOutlook As Object
Dim objMailMessage As Object
Set objOutlook = CreateObject("Outlook.Application")
Dim row As Range
Dim table As ListObject
Dim row_index As Long
Dim strEmail As String
Dim strExeptionID As String
ActiveWorkbook.Sheets("New 0-30").Select
Set table = ActiveSheet.ListObjects("New_030_Table")
For row_index = 1 To table.DataBodyRange.Rows.Count
strEmail = table.DataBodyRange(row_index, 3).Value
strExceptionID = table.DataBodyRange(row_index, 2).Value
If Not dict_emails.Exists(strEmail) Then
' first time we have seen this name
dict_emails.Add strEmail, strExceptionID
Else
dict_emails(strEmail) = dict_emails(strEmail) & vbCrLf & strExceptionID
End If
Next
Dim var_key As Variant
For Each var_key In dict_emails.Keys
Set objMailMessage = objOutlook.CreateItem(0) ' create new mail
With objMailMessage
.To = "" & var_key
.CC = ""
.BCC = ""
.Subject = "Exceptions Set to Expire in Less Than 30 Days"
.Body = "You have the following exceptions set to expire: " & vbCrLf & dict_emails(var_key)
.Save ' save as draft
End With
Next
End Sub
I was in same issue and I foung solution !!
but anyone will not believe in reference list "Microsoft Scripting Runtime" set priority to top 3rd. and it will start working and error will be gone.
I have been trying to work on powerpoint that has an excel database in background.
For now I am having trouble passing sheets as arguement in PPT VBA. The function lastrow and lastcoulmn return an error that "user-defined type not defined". Help would be appreciated. thanks.
Dim oXLApp As Object
Dim oWb As Object
Dim Deps As Excel.Range
Dim Dep, Shift, Name, EmpNo, Sup As String
Dim Sups As Excel.Range
Dim Shifts As Excel.Range
Public Sub getexceldata()
Dim str As String
Set oXLApp = CreateObject("Excel.Application")
Set oWb = oXLApp.Workbooks.Open(ActivePresentation.Path & "\" & "Property Wide.xlsm")
'Shifts
Set Shifts = oWb.Sheets(4).Range("A1:A" & lastRow(oWb.Sheets(4), "a"))
'departments
Set Deps = oWb.Sheets(3).Range("A1:" & Chr(lastColumn(oWb.Sheets(3), "1") + 64) & "1")
'supervisors
End Sub
Public Function lastRow(ByVal SheetA As Excel.Application.Sheet, Optional Columnno As Char = "/") As Long
If (Columnno = "/") Then
Set lastRow = SheetA.UsedRange.Row - 1 + SheetA.UsedRange.Rows.Count
Else
Set lastRow = SheetA.Range(Columno & Rows.Count).End(xlUp).Row
End If
End Function
Public Function lastColumn(ByVal SheetA As Excel.Application.Sheet, Optional rowno As Char = "/") As Integer
If (rowno = "/") Then
Set lastColumn = SheetA.UsedRange.Column - 1 + SheetA.UsedRange.Columns.Count
Else
Set lastColumn = SheetA.Cells(rowno, Columns.Count).End(xlToLeft).Column
End If
End Function
The first issue is that CHAR is not a valid variable type so I would suggest changing this to string.
Next make sure to include the Microsoft Office Excel 14.0 Object Library in your code reference.
With that you can make some slight adjustment to your code an everything should work.
Dim oXLApp As Object
Dim oWb As Object
Dim Deps As Excel.Range
Dim Dep, Shift, Name, EmpNo, Sup As String
Dim Sups As Excel.Range
Dim Shifts As Excel.Range
Public Sub getexceldata()
Dim str As String
Set oXLApp = CreateObject("Excel.Application")
Set oWb = oXLApp.Workbooks.Open(ActivePresentation.Path & "\" & "Property Wide.xlsm")
'Shifts
Set Shifts = oWb.Sheets(4).Range("A1:A" & lastRow(oWb.Sheets(4), "a"))
'departments
Set Deps = oWb.Sheets(3).Range("A1:" & Chr(lastColumn(oWb.Sheets(3), "1") + 64) & "1")
'supervisors
End Sub
Public Function lastRow(ByVal SheetA As Worksheet, Optional Columnno As String = "/") As Long
If (Columnno = "/") Then
Set lastRow = SheetA.UsedRange.Row - 1 + SheetA.UsedRange.Rows.Count
Else
Set lastRow = SheetA.Range(Columno & Rows.Count).End(xlUp).Row
End If
End Function
Public Function lastColumn(ByVal SheetA As Worksheet, Optional rowno As String = "/") As Integer
If (rowno = "/") Then
Set lastColumn = SheetA.UsedRange.Column - 1 + SheetA.UsedRange.Columns.Count
Else
Set lastColumn = SheetA.Cells(rowno, Columns.Count).End(xlToLeft).Column
End If
End Function
With that you should have what you need.