AutoRunning Upon Open Issue - vba

First off, I'm not very savvy when it comes to programming in VBA. I've done a lot of programming in the past in Java, C++, C#, and a few others so I'm not completely new to programming in general.
My current issue is that I have an inventory list that I want a notification to be emailed from once a certain quantity has reached a certain value. The email notification is setup and working fine except when the worksheet is closed then reopened, the notification will be sent again if the value has not gone above what was previously set. For example, if the quantity is at or below 1, a notification will be emailed. If that quantity is not changed to 2+ before the worksheet is saved and closed, the next time the sheet is opened, the notification will be sent again.
I want to limit this so that only when the quantity is changed for the first time then the notification is sent.
Code below:
Public Function EmailNotification(model As String, color As String, cell1 As Range)
Dim OutApp As Object
Dim OutMail As Object
Dim cell As Range
Application.ScreenUpdating = False
Set OutApp = CreateObject("Outlook.Application")
On Error GoTo cleanup
If cell1.Value <= 1 Then
'For Each cell In Columns("R").Cells.SpecialCells(xlCellTypeConstants)
'If cell.Value Like "?*#?*.?*" And _
'LCase(Cells(cell.Row, "S").Value) = "yes" Then
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = "username#domain.com"
.Subject = "Excel Notification: Toner Renewal"
.Body = "Dear Team," & _
vbNewLine & vbNewLine & _
"Please prep an order for " & color & " toner for a Dell " & model & vbNewLine & vbNewLine & _
"Quantity Remaining: " & cell1 & vbNewLine & vbNewLine & _
"Notification Sent: " & Now() & " from " & ActiveWorkbook.FullName
.Send
End With
On Error GoTo 0
Set OutMail = Nothing
'End If
'Next cell
End If
cleanup:
Set OutApp = Nothing
Application.ScreenUpdating = True
End Function
cell1 is the quantity that gets checked in the function.
I'm not sure if there is a way to halt this AutoRun at the startup then allow it to run again whenever one of the quantities is changed.
Thanks in advance for your help!

I'd take a cell in your workbook (say cell2) and store a boolean to say if the email has already been sent once or not.
Your function would look something like this:
Public Function EmailNotification(model As String, color As String, cell1 As Range, cell2 As Range)
Dim notifSent As Boolean: notifSent = cell2.Value '<-- get the value of cell2
If cell1.Value <= 1 And Not notifSent 'if quantity below threshold and no notification yet sent
'you send the email for the first time...
cell2.Value = True 'and you set this information in your cell
ElseIf cell1.Value > 1 And notifSent 'if quantity above threshold and notification has been sent before
cell2.Value = False 'reset notification to false
End If

Related

why does my VBA code that works in module not work as expected when assigned to a worksheet and a button

I have a workbook that is essentially an automated test, marking and feedback tool for end of topic tests for students. On the '701Test' sheetThey input their teaching group via a drop down list and the select their from subsequent list. They answer the multiple choice questions and press a button when finished. The button takes them to a 'results' page which gives their marks for each question, give feedback for incorrect answers and gives a total score. They then hit the finish button which generates a PDF copy of the mark sheet in their my documents folder and then emails a copy to themselves and the Schools email account. At this point I also wanted to post the final score to the students record on a central registry using a loop through the student list to find the name and offset to post the Score value from the 'Results' page and finally return to the test page. This last bit I wrote the code for in a module and it executes perfectly, but when added to the main code and run from the button the loop part fails to execute but the return to the test page does work, but no error is recorded for the loop failure.
Here is the 'Results' page code in full the 'With Central reg' bit at the bottom is the problem, any help is greatly appreciated.
Private Sub CommandButton1_Click()
Dim IsCreated As Boolean
Dim PdfFile As String, Title As String
Dim OutlApp As Object
Dim cell As Range
Dim Students As Range
Title = Range("D1").Value
sname = Range("B2").Value
PdfFile = CreateObject("WScript.Shell").SpecialFolders("MyDocuments") & "\" & sname & Title & ".pdf"
With ActiveSheet
.ExportAsFixedFormat Type:=xlTypePDF, Filename:=PdfFile, Quality:=xlQualityStandard, IncludeDocProperties:=True, IgnorePrintAreas:=False, OpenAfterPublish:=False
End With
On Error Resume Next
Set OutlApp = GetObject(, "Outlook.Application")
If Err Then
Set OutlApp = CreateObject("Outlook.Application")
IsCreated = True
End If
OutlApp.Visible = True
On Error GoTo 0
With OutlApp.CreateItem(0)
.Subject = Title
.to = Range("B2").Value ' <-- Put email of the recipient here"
.CC = "" ' <-- Put email of 'copy to' recipient here
.Body = "Hi," & vbLf & vbLf _
& "Yr 7 701 EOT test attached in PDF format." & vbLf & vbLf _
& "Regards," & vbLf _
& "KDS ICT Dept" & vbLf & vbLf
.Attachments.Add PdfFile
Application.Visible = True
.Display
End With
If IsCreated Then OutlApp.Quit
Set OutlApp = Nothing
With CentralReg
For Each cell In Range("A2:A250")
If cell = Range("Results!B2").Value Then
cell.Offset(0, 4).Activate
ActiveCell.Value = Range("Results!B27").Value
End If
Next
End With
End Sub
I believe you are trying to refer to CentralReg which is a worksheet, which means you should qualify it as such.
Also, you should not dim variables that are similar to defined objects/properties in VBE. Try MyCell instead of cell (good practice, not required).
I am assuming you want to see if the value on sheet CentralReg in Column A is equal to sheet Result B2. If this condition is met, your MyCell will take on the value equal sheet Result B27
Dim MyCell As Range
Dim Result, NewValue as Variant
Result = ThisWorkbook.Sheets("Result").Range("B2")
NewValue = ThisWorkbook.Sheets("Result").Range("B27")
With ThisWorkbook.Sheets("CentralReg")
For Each MyCell In .Range("A2:A250")
If MyCell = Result Then MyCell.Offset(, 4) = NewValue
Next MyCell
End With
That with statement is useless as nothing actually uses it within the construct.
Delete with CentralReg and End with and it will work.
alternatively if CentralReg IS something like a sheet then you need to precede your code with a . so this: Range("A2:A250") becomes this: .Range("A2:A250") and so on, the . tells the code that it is related to whatever your with construct surrounds

Email loop causing Notes to crash (Embed object = issue)

I have the following code which is always causing IBM(LOTUS) Notes to crash at the .EmbedObject line
Call body.EmbedObject(1454, "", Attachment)
This is the part of the main code. At this point there are 2 dictionaries which are converted to arrays and then into e-mail strings. The call to the EMAIL sub-routine is below.
Anyone have any idea what could be causing this or know a fix?? All variables are declared at the public level in the main module with string type
This works fine with a simple loop macro that I used to integrate into my macro (basic for loop calling the email routine every iteration, with declaring the document and body each time)
thank you
Private Sub SaveFilestoDesktop_andEmail()
'Saves file to desktop with date stamp and e-mails to the user
Dim WB As Workbook
Dim wks As String
Dim fname As String, i As Integer
Dim EmailArray_PC() As Variant, EmailArray_PM() As Variant
EmailArray_PM = dict.keys()
EmailArray_PC = dict_2.keys()
i = 1
Subj = "Items to Review"
'EmailBody = "The following items have been flagged as possible cost errors " & _
'"by process of identifying variances of +/- 30 % compared to the current average cost. " & _
'"Please see attachment and review for internal purposes." & vbLf & _
'vbLf & VBA.Format(Now, "m/d/yyyy hh:mm:ss AM/PM")
On Error GoTo errhandlr
For Each WB In Workbooks
'Set the first sheet name of each WB to the wks variable
wks = WB.ActiveSheet.Name
'If unsaved workbook (only part of the above sub procedures)
If Left(WB.Name, 4) = "Book" Then
fname = Application.DefaultFilePath & "\" & Replace(WB.Worksheets(1).Name, ".", "") & "- " & VBA.FormatDateTime(Date, vbLongDate) _
& " (" & Format(Time, "hhmmss AMPM") & ")"
With WB
' If Dir(fname) <> "" Then
Application.DisplayAlerts = False
'Save the file as an .xlsx to the default user path
.SaveAs Filename:=fname, FileFormat:=51
Application.DisplayAlerts = True
On Error Resume Next 'if tries to e-mail but it fails (such as for "blank")
'Setting up parameters for e-mailing
SendTo = Right(EmailArray_PM(i), Len(EmailArray_PM(i)) - WorksheetFunction.Find(",", EmailArray_PM(i)) - 1) & "_" & _
Left(EmailArray_PM(i), WorksheetFunction.Find(",", EmailArray_PM(i)) - 1) & "#quadra.ca"
SendCC = Right(EmailArray_PC(i), Len(EmailArray_PC(i)) - WorksheetFunction.Find(",", EmailArray_PC(i)) - 1) & _
"_" & Left(EmailArray_PC(i), WorksheetFunction.Find(",", EmailArray_PC(i)) - 1) & "#quadra.ca"
Attachment = WB.Name
'Call e-mail maco in Other module
Call Email_using_Notes_Call(SendTo, SendCC, Attachment)
'Increment i by 1
i = i + 1
On Error GoTo 0
'Close the Workbook, go to next WB
.Close
End With
'Clear the filename to save with for next WB
fname = Empty
End If
Next WB
Exit Sub
Erase EmailArray_PC: Erase EmailArray_PM
Set dict = Nothing: Set dict_2 = Nothing 'clear dict objs
errhandlr:
MsgBox err.Number & Space(2) & err.Description
err.Clear
'MsgBox err.Number & Space(2) & err.Description
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Call to EMAIL loop:
Sub Email_using_Notes_Call(ByVal SendTo As String, _
Optional ByVal SendCC As String, Optional ByVal Attachment As String)
On Error Resume Next
'Creates the Notes Document (e-mail)
Set doc = db.CreateDocument
With doc
.Subject = Subj
.SendTo = SendTo
.CopyTo = SendCC
.Importance = "1"
End With
'Creating the body of the Notes document
Set body = doc.CreateRichTextItem("Body")
'Formatting the body of the text
Call body.AppendText("The following items have been flagged as possible cost errors by process of identifying variances of +/- 30 %")
Call body.AddNewline(1) '--> This adds a line feed to the body
Call body.AppendText("compared to the current average cost. Please see attachment and review for internal purposes ")
Call body.EmbedObject(1454, "", Attachment) --> this is where it crashes 'EMBED_ATTACHMENT[1454 = embed attachment, 1453 = embed object]
Call body.AddNewline(2)
Call body.AppendText(Now())
Call doc.Send(False) 'False is the variable that indicates attach form or not (always false in our case)
'Clearing for next document
Set body = Nothing
Set doc = Nothing
On Error GoTo -1
End Sub
I think this issue is caused what you are trying to embed.
The document you are trying to Embed is the Excel workbook itself. You have the workbook open, so it cannot necessarily be read due to a lock.
Something that might help you definitely find out if that's the reason:
Try to add another file as the attachment that isn't open and see if it works, as a test.
Change the On Error Resume Next located in your e-mailing function to an error handler, like you have in the function above it.

Emailing Ranges Instead Of Rows

I am attempting to run through sheet 2 of an Excel workbook to email ranges to customers.
The ranges would be A1:B30,C1:D30,E1:F30 and so on with their account number in A1 & email in B1 and information below.
Every time I try to run the email it comes up with:
Run Time Error 1004
and then goes on to error
Object has been moved or deleted
Is there another way of emailing ranges or a way to amend this code?
Sub EmailRanges()
Dim cr As Range
Set cr = [b1]
ActiveWorkbook.EnvelopeVisible = True
Do While cr <> ""
cr.Offset(, -1).Resize(30, 2).Select
With ActiveSheet.MailEnvelope
.Introduction = " Good Morning"
.Item.To = cr
.Item.Subject = "Just testing, sorry for filling you inbox ^_^ "
.item.Send ' to send
.Item.Display ' to test
End With
MsgBox cr & " receives " & Selection.Address
Set cr = cr.Offset(, 2)
Loop
Application.ScreenUpdating = True
MsgBox "The Customers Have Been Notified"
End Sub
You need to be more explicit about your references (workbook, sheet, ...).
Thx to #Ralph :
A range can be only selected if the sheet is activated first. Otherwise, you'll get an error.
This run smoothly on my computer :
Sub Email_Ranges()
Dim rG As Range
Dim RangeToSend As Range
Dim CustomerMail As String
Set rG = ActiveWorkbook.ActiveSheet.[b1]
ActiveWorkbook.EnvelopeVisible = True
Do While rG.Value <> vbNullString
CustomerMail = rG.Value
Set RangeToSend = rG.Offset(, -1).Resize(30, 2)
'With RangeToSend.Parent.MailEnvelope
''Uncomment below if you get an error
rG.Parent.Activate
RangeToSend.Select
With Selection.Parent.MailEnvelope
.Introduction = "Good Morning"
With .Item
.To = CustomerMail
.Subject = "Just testing, sorry for filling your inbox ^_^ "
.display 'to test
.Send 'to send
End With
End With
Debug.Print CustomerMail & " receives " & RangeToSend.Address
Set rG = rG.Offset(, 2)
Loop
ActiveWorkbook.EnvelopeVisible = False
End Sub

Referencing an email address from a different sheet

I am having some trouble referencing an email address from a different sheet so I can send an email when a certain name is selected and "open" is chosen from a drop down box it will automatically send an email to that person. I have so far:
Sub Macro1()
Dim OutApp As Object
Dim OutMail As Object
Dim cell As Range
Application.ScreenUpdating = False
Set OutApp = CreateObject("Outlook.Application")
On Error GoTo cleanup
For Each cell In Columns("M").Cells.SpecialCells(xlCellTypeConstants)
If cell.Value Like "?*#xyz.com" And _
LCase(Cells(cell.Row, "N").Value) = "open" Then
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = cell.Value
.Subject = "Open Issue"
.Body = "Dear " & Cells(cell.Row, "J").Value _
& vbNewLine & _
"Issue raised: " & Cells(cell.Row, "C").Value _
& vbNewLine & _
"Regards"
.Send
End With
On Error GoTo 0
Set OutMail = Nothing
End If
Next cell
cleanup:
Set OutApp = Nothing
Application.ScreenUpdating = True
End Sub
This works if I input an email address in to column M manually but I am trying to have it so that when a name is selected from a drop down box in J it matches up the email to the name and sends it when "open" is selected from column N.
I have created a table with names and emails in another sheet which M uses VLookup to reference using =VLOOKUP(J3,Team!B5:E8,4,FALSE). I have tried adding HYPERLINK in front of VLOOKUP but it still doesn't create a link.
I also tried creating the email address from the names using split and concatenate but to no avail.
Your problem is that you use SpecialCells(xlCellTypeConstants). Once you made column M a formula your code will now ignore all the cells that have formulas in column M.
You can adjust the xlCellTypeConstants to xlCellTypeFormulas or just get rid of SpecialCells altogether to include both.

VBA Excel - If cell in one column contains email address and cell in another column is "SOLD" , send email

Just started experimenting with VBA today. Creating Excel sheet to track SOLD, PENDING, LOST that will allow salesmen to click a button to send a group email to one category at a time. After much searching I found some code that works well to send group email by checking a column to make sure a proper address is there. I found some other code that I thought would check the "Job Status" column so that only the "SOLD" or whatever would be chosen for email. I am a clueless beginner and need help. Here is the code that was working until I added the - If Sh.Cells(Target.Row, 7) = "PENDING" Then - part. Any help would be greatly appreciated. Thanks!
Private Sub CommandButton1_Click()
Dim cell As Range
Dim strto As String
For Each cell In ThisWorkbook.Sheets("Sales 2013").Range("E3:E500")
If cell.Value Like "?*#?*.?*" Then
If Sh.Cells(Target.Row, 7) = "PENDING" Then
strto = strto & cell.Value & ";"
End If
End If
Next cell
If Len(strto) > 0 Then strto = Left(strto, Len(strto) - 1)
Application.ScreenUpdating = False
Set OutApp = CreateObject("Outlook.Application")
OutApp.Session.Logon
On Error GoTo cleanup
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = "Anchor Sales"
.Bcc = strto
.Subject = "Enter subject here"
.Body = "" ' EMPTY FOR NOW
'USE THIS FOR ENTERING NAMES OF RECIPIENTS IN BODY TEXT "here"
'"Dear" & Cells(cell.Row, "A").Value _
& vbNewLine & vbNewLine & _
"Enter body text " & _
"here"
'You can add files also like this
'.Attachments.Add ("C:\test.txt")
'.Send 'Or use Display
.Display
End With
On Error GoTo 0
Set OutMail = Nothing
cleanup:
Set OutApp = Nothing
Application.ScreenUpdating = True
End Sub
Private Sub CommandButton2_Click()
End Sub
Please help!
of course I would prefer to send one mail per Person (if the recipients shall not know each other), but let's stay with your Problem.
you only need minor changes:
Private Sub CommandButton1_Click()
Dim cell As Range
Dim strto As String
For Each cell In ThisWorkbook.Sheets("Sales 2013").Range("E3:E500")
If cell.Value = "PENDING" Then strto = strto & cell.offset(0, 1).value & ";"
I am not sure if you Need the part "If cell.Value Like "?#?.?*" " or if that is from copy-paste from the code you found... If you Need it, the last line would have to be replaced by
If cell.Value Like "?*#?*.?*" and cell.Value = "PENDING" Then strto = strto & cell.offset(0, 1).value & ";"
'in Offset(0,1) I assume the mailadress is in the cell next to the cell tested for "pending"
Next cell
If Len(strto) > 0 Then strto = Left(strto, Len(strto) - 1)
the rest as you have it.
the Problem was caused by
If Sh.Cells(Target.Row, 7) = "PENDING" Then
as you have no definition for "sh" - but you also don't need it.
I hope this helps