sending an auto mail from excel based on multiple conditions - excel

I am a VBA novice so thanks in advance for anyone who can help me here. Basically I am using an adapted Ron de Bruin piece of code to automatically send a mail to students when their attendance drops below a certain level as displayed in a particlular excel cell. So far, so good, the Ron de Bruin stuff looks after this.
But there is another criterion which I want to add and that is basically to only send the mail if there is also a letter 'Y' in a different cell in the same row as the attendance.
To summarize, I only want the mail to go to people who fulfill the two criteria, 1)dropping below a certain level, and 2)having a 'Y' in another cell, but the code at the moment only accounts for the first criterion. Huge thanks. Alun (code below)
Option Explicit
Private Sub Worksheet_Calculate()
Dim FormulaRange As Range
Dim NotSentMsg As String
Dim MyMsg As String
Dim SentMsg As String
Dim MyLimit As Double
NotSentMsg = "Not Sent"
SentMsg = "Sent"
'Above the MyLimit value it will run the macro
MyLimit = 80
'Set the range with Formulas that you want to check
Set FormulaRange = Me.Range("BH279:BH280")
On Error GoTo EndMacro:
For Each FormulaCell In FormulaRange.Cells
With FormulaCell
If IsNumeric(.Value) = False Then
MyMsg = "Not numeric"
Else
If .Value < MyLimit Then
MyMsg = SentMsg
If .Offset(0, 1).Value = NotSentMsg Then
Call Mail_with_outlook2
End If
Else
MyMsg = NotSentMsg
End If
End If
Application.EnableEvents = False
.Offset(0, 1).Value = MyMsg
Application.EnableEvents = True
End With
Next FormulaCell
ExitMacro:
Exit Sub
EndMacro:
Application.EnableEvents = True
MsgBox "Some Error occurred." _
& vbLf & Err.Number _
& vbLf & Err.Description
End Sub

If .Value2 < MyLimit And Not .EntireRow.Find(What:="Y", LookIn:=xlValues, LookAt:=xlWhole) Is Nothing Then Call Mail_with_outlook2
'If you look for the complementer solution, remove the " Not"
You look for the value "Y" in the same row.
I'd recommend setting output variables as well to the mailing macro Call Mail_with_outlook2(emailaddress, name, title, MyValue).

Related

Copy row from one sheet to another when a cell value is negative

I have code in which if column I, it will look at that value and then send an email if it matches any of my limits. Then I have also included a refresh anytime I edit another column.
This code is in ThisWorkbook
Private Sub Workbook_Open()
Call Worksheet_Calculated
End Sub
This is my code in Sheet1 that will run the operation if anything in column G changes.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address Like "$F$*" Then
Call Worksheet_Calculated
End If
End Sub
This code is in a module. It checks to see if any value in column I is any of of the MyLimit Values, if it is, in column I, it will say sent or not sent. If sent, an email will be generated.
Option Explicit
Public Sub Worksheet_Calculated()
Dim FormulaRange As Range
Dim NotSentMsg As String
Dim MyMsg As String
Dim SentMsg As String
Dim MyLimita As Double
Dim MyLimitb As Double
Dim MyLimitc As Double
Dim MyLimitd As Double
NotSentMsg = "Not Sent"
SentMsg = "Sent"
'Above the MyLimit value it will run the macro
MyLimita = 100
MyLimitb = 50
MyLimitc = 10
MyLimitd = 1
'Set the range with Formulas that you want to check
'This is the column that shows how many days left
Set FormulaRange = Range("H5:H25")
On Error GoTo EndMacro:
For Each FormulaCell In FormulaRange.Cells
With FormulaCell
If IsNumeric(.Value) = False Then
MyMsg = ""
ElseIf (.Value = MyLimita Or .Value = MyLimitb Or .Value = MyLimitc Or .Value = MyLimitd) Then
MyMsg = SentMsg
If .Offset(0, 1).Value = NotSentMsg Then
Call Mail_with_outlook(FormulaCell)
End If
Else
MyMsg = NotSentMsg
End If
Application.EnableEvents = False
.Offset(0, 1).Value = MyMsg
Application.EnableEvents = True
End With
Next FormulaCell
ExitMacro:
Exit Sub
EndMacro:
Application.EnableEvents = True
MsgBox "Some Error occurred." _
& vbLf & Err.Number _
& vbLf & Err.Description
End Sub
The above code will call another module which will populate the email that I would like to send.
Option Explicit
Public FormulaCell As Range
Public Sub Mail_with_outlook(FormulaCell As Range)
Dim OutApp As Object
Dim OutMail As Object
Dim strto As String, strcc As String, strbcc As String
Dim strsub As String, strbody As String
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
'Change the parenthesis for column that the email is in
strto = Cells(FormulaCell.Row, "K").Value
strcc = ""
strbcc = ""
strsub = "Payment Notification (PO --Enter PO # Here--)"
'Change the parenthesis for the Column that the POC is in
strbody = "Hi " & Cells(FormulaCell.Row, "J").Value & vbNewLine & vbNewLine & _
"This is a reminder to pay for a licensing/maintenance bill in: " & Cells(FormulaCell.Row, "H").Value & " days." & _
vbNewLine & vbNewLine & "Line 2" & _
vbNewLine & "Line 3" & _
vbNewLine & "Line 4" & _
vbNewLine & "Line 5" & _
vbNewLine & "Line 6"
With OutMail
.To = strto
.CC = strcc
.BCC = strbcc
.Subject = strsub
.Body = strbody
'You can add a file to the mail like this
'.Attachments.Add ("C:\test.txt")
.Display ' or use .Send
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
I want to continue running this code on start up and whenever column G is edited, but only in Sheet1.
I want when column I is negative to copy that entire row into sheet2.

VBE Start macro on startup and then after that, once a certain column (G) is edited everyday

In VBE, I have a spreadsheet where if the column 'I' reaches 'MyLimit_', then i will automatically get an email. I am trying to make this code run only when I change a certain column (G).
This code is for the sheet.
In column I, I am looking at the these values to see if they equal my limit. if they do, it will trigger and email being generated.
Option Explicit
Private Sub Worksheet_Calculate()
Dim FormulaRange As Range
Dim NotSentMsg As String
Dim MyMsg As String
Dim SentMsg As String
Dim MyLimita As Double
Dim MyLimitb As Double
Dim MyLimitc As Double
Dim MyLimitd As Double
NotSentMsg = "Not Sent"
SentMsg = "Sent"
'Above the MyLimit value it will run the macro
MyLimita = 100
MyLimitb = 50
MyLimitc = 10
MyLimitd = 1
'Set the range with Formulas that you want to check
'This is the column that shows how many days left
Set FormulaRange = Me.Range("I5:I25")
On Error GoTo EndMacro:
For Each FormulaCell In FormulaRange.Cells
With FormulaCell
If IsNumeric(.Value) = False Then
MyMsg = ""
ElseIf .Value = MyLimita Then
MyMsg = SentMsg
If .Offset(0, 1).Value = NotSentMsg Then
Call Mail_with_outlook
End If
ElseIf .Value = MyLimitb Then
MyMsg = SentMsg
If .Offset(0, 1).Value = NotSentMsg Then
Call Mail_with_outlook
End If
ElseIf .Value = MyLimitc Then
MyMsg = SentMsg
If .Offset(0, 1).Value = NotSentMsg Then
Call Mail_with_outlook
End If
ElseIf .Value = MyLimitd Then
MyMsg = SentMsg
If .Offset(0, 1).Value = NotSentMsg Then
Call Mail_with_outlook
End If
Else
MyMsg = NotSentMsg
End If
Application.EnableEvents = False
.Offset(0, 1).Value = MyMsg
Application.EnableEvents = True
End With
Next FormulaCell
ExitMacro:
Exit Sub
EndMacro:
Application.EnableEvents = True
MsgBox "Some Error occurred." _
& vbLf & Err.Number _
& vbLf & Err.Description
End Sub
This code is what I put into the module. This is basically all the code I used to generate my email and then populate with the appropriate info.
Option Explicit
Public FormulaCell As Range
Sub Mail_with_outlook()
Dim OutApp As Object
Dim OutMail As Object
Dim strto As String, strcc As String, strbcc As String
Dim strsub As String, strbody As String
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
'Change the parenthesis for column that the email is in
strto = Cells(FormulaCell.Row, "L").Value
strcc = ""
strbcc = ""
strsub = "Payment Notification (PO --Enter PO # Here--)"
'Change the parenthesis for the Column that the POC is in
strbody = "Hi " & Cells(FormulaCell.Row, "K").Value & vbNewLine & vbNewLine & _
"This is a reminder to pay for a licensing/maintenance bill in: " & Cells(FormulaCell.Row, "I").Value & " days." & _
vbNewLine & vbNewLine & "Line 2" & _
vbNewLine & "Line 3" & _
vbNewLine & "Line 4" & _
vbNewLine & "Line 5" & _
vbNewLine & "Line 6"
With OutMail
.To = strto
.CC = strcc
.BCC = strbcc
.Subject = strsub
.Body = strbody
'You can add a file to the mail like this
'.Attachments.Add ("C:\test.txt")
.Display ' or use .Send
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
You can run a code automatically when the workbook is opened by calling it from the Workbook_Open() procedure.
The Worksheet_Change(ByVal Target As Range) procedure fires every time there's a chance, and then you can use the Address property of Target to see if the change was within Column G.
In the ThisWorkbook module:
Private Sub Workbook_Open()
Call YourProcedureName
End Sub
In a Sheet module:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address Like "$G$*" Then
Call YourProcedureName
End If
End Sub
In a regular module:
Public Sub YourProcedureName()
'Your code here
End Sub

Copy list of emails to Outlook

Can someone help me with below code? Here is a piece of code that is intended to copy a list of emails id's from "Sheet1" cells "B2" to "n" number of rows having data.
I am facing two issues with this.
1) HTMLBody text is not copied to email.
2) List of email recipient available at Sheet1, B2 onward is not getting copied on email recipient list ("To" list).
Thanks in advance!
Sub MeetingMacro()
'MsgBox Hour(Now)
If Weekday(Now, vbMonday) >= 6 And Hour(Now) > 12 Then
Exit Sub
End If
Application.ScreenUpdating = False
Dim pt As PivotTable
Set pt = ThisWorkbook.Sheets("Sheet2").PivotTables("PivotTable")
pt.RefreshTable
Application.CalculateUntilAsyncQueriesDone
Call saveAsXlsx1
Application.CalculateUntilAsyncQueriesDone
Call savefile
Application.CalculateUntilAsyncQueriesDone
Call Send_Range
'Call Send_Range
End Sub
Sub Send_Range()
Dim TBL As ListObject
ThisWorkbook.Activate
ThisWorkbook.EnvelopeVisible = False
ThisWorkbook.Sheets("Sheet2").Range("A1:B30").Select
ThisWorkbook.Activate
With ActiveSheet.MailEnvelope
SDest = ""
For iCounter = 2 To WorksheetFunction.CountA(Columns(3))
If SDest = "" Then
SDest = Cells(iCounter, 3).Value
SDest.Value.Select
Else
SDest = SDest & ";" & Cells(iCounter, 3).Value
End If
Next iCounter
.Item.To = SDest
.Item.CC = "someone#example.com"
.Item.Subject = "[URGENT] Meeting has been cancelled. "
.Item.HTMLBody = "Hello," & vbCrLf & "Meeting has been cancelled. Fresh invite will be sent soon.” & vbCrLf & "Regards"
.Item.Attachments.Add "C:\Attachment.xlsx" 'ActiveWorkbook.FullName
.Item.Send
End With
'MsgBox (TimeOfDay)
End Sub
'MsgBox (TimeOfDay)
Sub savefile()
Application.ScreenUpdating = False
ThisWorkbook.Activate
Application.ScreenUpdating = True
ThisWorkbook.Save
End Sub
Sub saveAsXlsx1()
ThisWorkbook.Worksheets(Array("Sheet2")).Copy
Application.DisplayAlerts = False
ActiveSheet.Shapes.Range("FetchData").Delete
ActiveWorkbook.SaveAs Filename:="C:\Attachment.xlsx"
ActiveWorkbook.Close
End Sub
Sub Meeting4()
ThisWorkbook.Application.DisplayAlerts = False
ActiveWorkbook.Save
ThisWorkbook.Close
End Sub
Say you have cells B2:B30 (all in the same column) in Sheet1, containing email addresses. What you want is to grab the values in these cells, and turn them into a one-dimensional array - that's done like this:
Dim values As Variant
values = Application.WorksheetFunction.Transpose(Sheet1.Range("B2:B30").Value)
With a one-dimensional array of email addresses, all you need to do is to turn it into a String. The Join function is made exactly for that:
Dim recipients As String
recipients = Join(values, ";")
That's all! ...assuming the cells all contain an email address string. If one cell contains an error value, expect trouble. If there are blanks, expect blanks (shouldn't make a difference though). If the range to grab isn't carved in stone, research how to make it more dynamic.
The HtmlBody is expecting an HTML-encoded string that contains HTML markup. If you only have plain text, use the Body property instead.

VBA - Remove Seconds from NOW function

I have something that notify me an hour before it happens. For that, I use the NOW function in VBA as I need it to check for the Date as well.
The problem is the script runs every 20 seconds so I can't have it consider seconds for the NOW function.
Is there a way to remove those? To have only like (DAY,MONTH,YEAR,HOUR,MINUTE)?
Something along those lines:
MyLimit = NOW(DAY,MONTH,YEAR,HOUR,MINUTE)
For Each FormulaCell In FormulaRange.Cells
With FormulaCell
If .Value = MyLimit Then
Call Notify
Here is the script in which I attempt to detect the date and time.
Option Explicit
Public Function AutoRun()
Application.OnTime Now + TimeValue("00:00:20"), "TaskTracker2"
End Function
Public Sub TaskTracker2()
Dim FormulaCell As Range
Dim FormulaRange As Range
Dim NotSentMsg As String
Dim MyMsg As String
Dim SentMsg As String
Dim SendTo As String
Dim CCTo As String
Dim BCCTo As String
Dim MyLimit As Date
NotSentMsg = "Not Sent"
SentMsg = "Sent"
SendTo = Range("D2")
CCTo = Range("E2")
BCCTo = Range("F2")
MyLimit = Format((Now), "DD/MM/YYYY HH:MM")
Set FormulaRange = Range("E5:E35")
On Error GoTo EndMacro:
For Each FormulaCell In FormulaRange.Cells
With FormulaCell
If .Value = MyLimit Then
MyMsg = SentMsg
If .Offset(0, 1).Value = NotSentMsg Then
strTO = SendTo
strCC = CCTo
strBCC = BCCTo
strSub = "[Task Manager] Reminder that you need to: " & Cells(FormulaCell.Row, "A").Value
strBody = "Hello Sir, " & vbNewLine & vbNewLine & _
"This email is to notify that you that your task : " & Cells(FormulaCell.Row, "A").Value & " with the following note: " & Cells(FormulaCell.Row, "B").Value & " is nearing its Due Date." & vbNewLine & "It would be wise to complete this task before it expires!" & _
vbNewLine & vbNewLine & "Truly yours," & vbNewLine & "Task Manager"
If sendMail(strTO, strSub, strBody, strCC) = True Then MyMsg = SentMsg
End If
Else
MyMsg = NotSentMsg
End If
Application.EnableEvents = False
.Offset(0, 1).Value = MyMsg
Application.EnableEvents = True
End With
Next FormulaCell
AutoRun
ExitMacro:
Exit Sub
EndMacro:
Application.EnableEvents = True
MsgBox "Some Error occurred." _
& vbLf & Err.Number _
& vbLf & Err.Description
End Sub
To strip the seconds off Now, you can use some maths or to-and-from text conversion.
CDate(format(Now, "dd-mmm-yyyy hh:mm"))
'... or,
CLng(Now * 1440)/1440
Both of those return a true, numerical datetime value with the seconds stripped off. They do not average the seconds to the nearest minute; simply remove them.
You could just round MyLimit to the nearest minute:
MyLimit = Round(Now * 1440, 0) / 1440
Consider, when comparing it to the contents of a cell, that you might need to use a <= or >= comparison to avoid problems if the time changes at the "wrong" time for an equality to hold true.
Another method would be this:
MyLimit = now-second(now)/60/60/24
second(now) returns the seconds, and the /60/60/24 converts it to days, which every date and time is stored in. Use this or Jeeped's answer, any one of these should work.
Edit:
To avoid the tiny but existing possibility of error, use this:
MyLimit = now
MyLimit =MyLimit -second(MyLimit)/60/60/24
Try limit = Format((Now), "DD/MM/YYYY HH:MM")
Use the Date function instead of the NOW function
https://msdn.microsoft.com/en-us/library/aa227520(v=VS.60).aspx
UPDATE
I usually just go with the function =TIME(HOUR(NOW()),MINUTE(NOW()),0)
Alternative approach per VBA Office 2010 and later:
Dim DateWithoutSeconds : DateWithoutSeconds = DateAdd("s",-Second(Now),Now)
Notice that the minus (-) removes the seconds.
More info at https://msdn.microsoft.com/en-us/library/office/gg251759.aspx

Run-Time error '1004' The specified value is out of range

Sub FindInShapes1()
Dim rStart As Range
Dim shp As Shape
Dim sFind As String
Dim sTemp As String
Dim Response
sFind = InputBox("Search for?")
If Trim(sFind) = "" Then
MsgBox "Nothing entered"
Exit Sub
End If
Set rStart = ActiveCell
For Each shp In ActiveSheet.Shapes
sTemp = shp.TextFrame.Characters.Text
If InStr(LCase(sTemp), LCase(sFind)) <> 0 Then
shp.Select
Response = MsgBox( _
prompt:=shp.TopLeftCell & vbCrLf & _
sTemp & vbCrLf & vbCrLf & _
"Do you want to continue?", _
Buttons:=vbYesNo, Title:="Continue?")
If Response <> vbYes Then
Set rStart = Nothing
Exit Sub
End If
End If
Next
MsgBox "No more found"
rStart.Select
Set rStart = Nothing
End Sub
Hi,
I made the above Macro for finding excel shapes in a "crouded" worksheet, by the text written inside. The macro works in any new books but not in the one I need, were it keeps on showing the following message:
"Run-Time error '1004'
The specified value is out of range"
and as soon as i click on "Debug" it highlights the line:
sTemp = shp.TextFrame.Characters.Text
What's wrong?
Thanks for your help
Chiara
Sorry to break the convention but the similar error I get:
The specified value is out of range
Run-time error -2147024809
In my scenario I am simply returning a shape as part of a GET property in side a class that store a Shape Object. The property works for Shape Type Text Boxes but craps out on sending back Line Shapes. As per below.
I cannot use the on error, Or don't know how because the error occur at End Property?
Public Property Get shp_Obj() As Shape
If prvt_int_Ordinal = 13 Them
MsgBox prvt_Shp_Shape.Name, , "prvt_Shp_Shape.Name"
Set shp_Obj = prvt_Shp_Shape
End If
End Property
I think as there is no way to check for the existence of a TextFrame within a shape, you should ignore the error by using On Error Resume Next:
Sub FindInShapes1()
Dim rStart As Range
Dim shp As Shape
Dim sFind As String
Dim sTemp As String
Dim Response
On Error Resume Next
sFind = InputBox("Search for?")
If Trim(sFind) = "" Then
MsgBox "Nothing entered"
Exit Sub
End If
Set rStart = ActiveCell
For Each shp In ActiveSheet.Shapes
'If shp.TextFrame.Characters.Count > 0 Then
If InStr(LCase(sTemp), LCase(sFind)) <> 0 Then
shp.Select
Response = MsgBox( _
prompt:=shp.TopLeftCell & vbCrLf & _
sTemp & vbCrLf & vbCrLf & _
"Do you want to continue?", _
Buttons:=vbYesNo, Title:="Continue?")
If Response <> vbYes Then
Set rStart = Nothing
Exit Sub
End If
End If
'End If
sTemp = shp.TextFrame.Characters.Text
Next
MsgBox "No more found"
rStart.Select
Set rStart = Nothing
End Sub
`
There is nothing wrong with your code. You will only get this error if the Active worksheet is password protected.
Can you check that?
Also check below url from so
Excel macro "Run-time error '1004"

Resources