CDO Email Automation - email

I have a MS Access 2010 db that I want to send emails from automatically. I have the query set up but am getting stuck with the CDO VBA. They query is called 'qryEmails' and contains the following 4 fields:
ReturnCode, SalesOrderNumber, Name, EmailAddress
How do I get Access to:
Loop through each record and send an email to each email address listed
In each email, have a message that will contain reference to the
first 3 fields, so each message appears personalised
Have a dynamic subject, so the ReturnCode field is in each subject
I have been trying small steps at first, so far I am receiving 100's of emails to the same address. Here is my code (I have used XXX where I do not want to disclose info):
Dim rst As ADODB.Recordset
Dim strSQL As String
Dim strEmail As String
Set rst = New ADODB.Recordset
'
strSQL = "[qryEmails]" 'source of recordset
rst.Open strSQL, CurrentProject.Connection, adOpenKeyset, adLockOptimistic
'
Do While Not rst.EOF
strEmail = rst.Fields("EmailAddress")
Set objMessage = CreateObject("CDO.Message")
objMessage.Subject = "Your refund is:" '
objMessage.FROM = """SENDER"" <XXX#somewhere.com>"
objMessage.To = rst.Fields("EmailAddress")
objMessage.TextBody = objMessage.TextBody & rst(1)
'==Add fields to email body
'Do While strEmail = rst.Fields("EmailAddress")
'rst.MoveNext
'If rst.EOF Then Exit Do
'Loop
' ========= SMTP server configuration
objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
'Name or IP of Remote SMTP Server
objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "XXX"
'Server port (typically 25)
objMessage.Configuration.Fields.Item _
("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
objMessage.Configuration.Fields.Update
'==End remote SMTP server configuration section==
'Send email
objMessage.Send
'Clear variable for next loop
Set objMessage = Nothing
Loop
rst.Close
Set rst = Nothing
Any idea why this is sending 100's of emails? The query result so far is only returning two addresses for testing purposes.

Within the loop, the recordset remains on the same row. And since the recordset row does not change, it never reaches rst.EOF
That code includes a disabled line for MoveNext. Uncomment that line. And you probably want to position it just before the Loop statement.
Do While Not rst.EOF
' do everything you need for current record,
' then move to the next record ...
rst.MoveNext
Loop

Related

send email with VBA with no user interaction with email client

I'm trying to get this code to send the attachment via email. But their will be several hundred attachments, so i don't want the user to need to interact with the email client. Any suggestions would be greatly appreciated.
Private Sub CreateReports_Click()
Dim x As String
Dim y As String
Dim StrSQL As String
Dim stWhereStr As String 'Where Condition'
Dim stSection As String 'Selection from drop down list
Dim stfile As String
Dim stDocName As String
Dim StrEmail As String
StrSQL = "SELECT DISTINCTROW [OPDA ISSR- Courts Users by District/Cir].[Sup], [OPDA ISSR- Courts Users by District/Cir].SupEmail " & _
"FROM [OPDA ISSR- Courts Users by District/Cir];"
y = Year(Date)
Dim db As DAO.Database
Dim rst As DAO.Recordset
Set db = CurrentDb
Dim qdTemp As DAO.QueryDef
Set qdTemp = db.CreateQueryDef("", StrSQL)
Set rst = qdTemp.OpenRecordset()
If rst.EOF And rst.BOF Then
MsgBox "No data available for the Ledger Process routine."
Else
Debug.Print rst.Fields.Count
rst.MoveFirst
Do While Not rst.EOF
x = rst![Sup]
StrEmail = rst![Supemail]
stDocName = "Courts - ISSR Recertification Report"
stWhereStr = "[OPDA ISSR- Courts Users by District/Cir].[SUP]= '" & x & "'"
stfile = "P:\DFI\FIB\Access Tables\FibCustomers\ISSR Reports\Courts\" & x & " - " & y & " FedInvest InvestOne Recertification.pdf"
DoCmd.OpenReport stDocName, acPreview, , stWhereStr
DoCmd.SendObject acSendReport, stDocName, acFormatPDF, StrEmail, , , "My Subject here", "your report"
DoCmd.OutputTo acOutputReport, stDocName, acFormatPDF, stfile
DoCmd.Close acReport, stDocName
rst.MoveNext
Loop
End If
rst.Close
Set rst = Nothing
End Sub
You could either use your local email client's API and open a hidden instance to create and send emails (e.g. Outlook, Lotus Notes) or you could use the CDO object.
http://www.rondebruin.nl/win/s1/cdo.htm
Even though the above example is in Excel, the code works great. The only thing you will have to make sure if you're using the CDO object is if the necessary ports are open. Also check the related threads to this question, tons of valid information if you want to go with a specific email client.
The best way to send unattended emails is to use SMTP. Bypassing the MAPI security prompts is not a good idea.
Our Total Access Emailer addin program uses SMTP so you can broadcast a large volume of emails for your users. Total Access Emailer lets you send personalized emails to everyone on your list and embed fields from your records in the message and subject. It can also attach Access reports as PDF files filtered for each recipient so that each person only sees their data. No programming is required with its Wizard interface. A VBA library is available if you want to automate it. More info here: http://www.fmsinc.com/MicrosoftAccess/Email.asp
A free trial is available: http://www.fmsinc.com/MicrosoftAccess/Email/free-trial.html

Trying to grab multiple fiedld from querydef and transfer to default email

I have been working with a database and received help getting the correct function to occur in that the code is working with a query and then looping through each report group (by name) and populating the fields of a report and saving the report by individual name. The last part of this which I have been struggling with. One of the fields in the recordset is Sup_Email, I’m trying to pass the report for sup with the email address in the above field to my default email client. I can’t figure how to isolate the second variable, keep it together with the right report and pass them both to email. Any help would be greatly appreciated.
Private Sub CreateReports_Click()
Dim x As String
Dim y As String
Dim z As String
Dim StrSQL
Dim StrEmail
Dim stWhereStr As String 'Where Condition'
Dim stSection As String 'Selection from drop down list
Dim stfile As String
StrSQL = "SELECT distinct[Sup] FROM ([OPDA ISSR- Courts Users by District/Cir])"
StrEmail = "SELECT [Sup_email] FROM ([OPDA ISSR- Courts Users by District/Cir])"
y = Year(date)
Dim db As DAO.Database
Dim rst As DAO.Recordset
Set db = CurrentDb
Dim qdTemp As DAO.QueryDef
Set qdTemp = db.CreateQueryDef("", StrSQL)
Set rst = qdTemp.OpenRecordset()
If rst.EOF And rst.BOF Then
MsgBox "No data available for the Ledger Process routine."
Else
rst.MoveFirst
Do While Not rst.EOF
x = rst![Sup]
z = rst![Sup_email]
stDocName = "Courts - ISSR Recertification Report"
stWhereStr = "[OPDA ISSR- Courts Users by District/Cir].[SUP]= '" & x & "'"
stfile = "P:\DFI\FIB\Access Tables\FibCustomers\ISSR Reports\Courts\" & x & " - " & y & " FedInvest InvestOne Recertification.pdf"
DoCmd.OpenReport stDocName, acPreview, , stWhereStr
DoCmd.OutputTo acOutputReport, stDocName, acFormatPDF, stfile
DoCmd.Close acReport, stDocName
rst.MoveNext
Loop
End If
rst.Close
Set rst = Nothing
End Sub
Each Sup has only one email address. The way it is setup is that a query (the OPDA ISSR- Courts Users by District/Cir query from above) pulls the first and last name of the supervisor and concatenates it into Sup, it also has their phone and email address (Sup_email), then has the employee, their accounts and access level, this information all applies to each field of the form. The direction I was sent yesterday created a query recordset (it's was continuing to loop). I tried then to add the field for email, and it's all gone awry since.
I have modified your code so that it uses the email address from the table and will create the report, then send.
Private Sub CreateReports_Click()
Dim x As String
Dim y As String
Dim StrSQL As String
Dim stWhereStr As String 'Where Condition'
Dim stSection As String 'Selection from drop down list
Dim stfile As String
Dim stDocName As String
Dim StrEmail As String
StrSQL = "SELECT DISTINCTROW [OPDA ISSR- Courts Users by District/Cir].[Sup], [OPDA ISSR- Courts Users by District/Cir].SupEmail " & _
"FROM [OPDA ISSR- Courts Users by District/Cir];"
y = Year(Date)
Dim db As DAO.Database
Dim rst As DAO.Recordset
Set db = CurrentDb
Dim qdTemp As DAO.QueryDef
Set qdTemp = db.CreateQueryDef("", StrSQL)
Set rst = qdTemp.OpenRecordset()
If rst.EOF And rst.BOF Then
MsgBox "No data available for the Ledger Process routine."
Else
Debug.Print rst.Fields.Count
rst.MoveFirst
Do While Not rst.EOF
x = rst![Sup]
StrEmail = rst![Supemail]
stDocName = "Courts - ISSR Recertification Report"
stWhereStr = "[OPDA ISSR- Courts Users by District/Cir].[SUP]= '" & x & "'"
stfile = "P:\DFI\FIB\Access Tables\FibCustomers\ISSR Reports\Courts\" & x & " - " & y & " FedInvest InvestOne Recertification.pdf"
DoCmd.OpenReport stDocName, acPreview, , stWhereStr
DoCmd.SendObject acSendReport, stDocName, acFormatPDF, StrEmail, , , "My Subject here", "your report"
DoCmd.OutputTo acOutputReport, stDocName, acFormatPDF, stfile
DoCmd.Close acReport, stDocName
rst.MoveNext
Loop
End If
rst.Close
Set rst = Nothing
End Sub

Pass an email address to the SendObject method in VBA

I would like to take the email address from my table 'GetContact_TempTbl" and send the report to that email address. This email will change according the company that recieves. I currently pull the related email address and store it in the temporary table. I currently get the Object Required error.
Many thanks in advace for the advice.
Dim db As Database
Dim rs As Recordset
Dim stRecipients As String
Dim stDocName As String
Set db = CurrentDb()
Set rs = db.OpenRecordset("GetContact_TempTbl")
Set stRecipients = rs.Fields("Contact_Email")
stDocName = "License CODs"
stRecipietns = stRecipients
DoCmd.SendObject acReport, stDocName, acFormatPDF, stRecipients, , , "Thank You for your purchase"
If your recordset holds one row for each recipient you want to email, walk the recordset to gather them instead of reading only the recipient from the first row.
Const stDocName As String = "License CODs"
Dim db As DAO.database
Dim rs As DAO.Recordset
Dim stRecipients As String
Set db = CurrentDb()
Set rs = db.OpenRecordset("GetContact_TempTbl")
With rs
Do While Not .EOF
stRecipients = stRecipients & ";" & !Contact_Email
.MoveNext
Loop
.Close
End With
If Len(stRecipients) > 0 Then
' discard leading ";"
stRecipients = Mid(stRecipients, 2)
DoCmd.SendObject acReport, stDocName, acFormatPDF, _
stRecipients, , , "Thank You for your purchase"
Else
MsgBox "No recipients to email!"
End If
Set rs = Nothing
Set db = Nothing
However, if my interpretation was incorrect, and the recordset always contains a single row with just one Contact_Email, you don't even need a recordset. You can simply retrieve the Contact_Email with DLookup.
stRecipients = Nz(DLookup("Contact_Email", "GetContact_TempTbl"), "")
You should only use set with an object, not a string:
Dim db As Database
Dim rs As Recordset
Dim stRecipients As String
Dim stDocName As String
Set db = CurrentDb()
Set rs = db.OpenRecordset("GetContact_TempTbl")
''This is not a field object, it is a string
stRecipients = rs.Fields("Contact_Email")
stDocName = "License CODs"
stRecipietns = stRecipients
DoCmd.SendObject acReport, stDocName, acFormatPDF, _
stRecipients, , , "Thank You for your purchase"
It may be possible to make this easier if you say how you create the temporary table.

Access 2010 - Cannot edit textbox in unbound form which is populated by recordset

I am using Access 2010 with linked tables from SQL Server 2008. I have a form that I am populating using a recordset and the control source of the textbox in this form is set to a field from the recordset. I find that although I can navigate through all the 16 records on the form, and the form loads correctly, I am unable to edit the Notes text box. It needs to be editable. The textbox has Enabled=True and Locked=False. The AllowEdits property of the form is set to true. All the tables in the query have primary keys. So, is it my query then - since it has right and inner joins in it? So the issue is that I cannot type in the textbox.
Just a little background, I tried using a query as the recordsource for this form, but found that Access' AutoSave feature inserted incomplete records into my Result table, in addition to the updates and inserts done by my Save button event. If the only way to circumvent this is to ask the user whether he/she would like to save changes every time he navigates, then that would be way too frustrating for the end user. So, I have had to use an unbound form where I use VBA to populate it using a ADO recordset.
Incidentally, I can edit the DocID and DocumentType columns, it's the fields from the query that cannot be changed (QCNote)
Here is the code from my Form_Open event. I also have a Form_Current event that disables the Submit button for inapplicable categories.
Private Sub Form_Open(Cancel As Integer)
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Set cn = CurrentProject.AccessConnection
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = cn
DocID = [Forms]![QCDocAttributes]![DocID]
DocumentType = [Forms]![QCDocAttributes]![Document Type]
strSQL = "SELECT " & DocID & " AS DocID,'" & DocumentType & "' AS DocumentType, QC_QCDecisionPoint.Description, QC_QCDecisionPoint.QCDecisionPointID , QC_QCResultDecisionPoint.QCNote FROM QC_QCResultDecisionPoint RIGHT JOIN ((QC_QCAttribute INNER JOIN QC_QCAttributeDecisionPointAsc ON QC_QCAttribute.QCAttributeID = QC_QCAttributeDecisionPointAsc.QCAttributeID) INNER JOIN QC_QCDecisionPoint ON QC_QCAttributeDecisionPointAsc.QCDecisionPointID = QC_QCDecisionPoint.QCDecisionPointID) ON QC_QCResultDecisionPoint.QCDecisionPointID = QC_QCDecisionPoint.QCDecisionPointID WHERE (((QC_QCAttribute.Description)= '" & [Forms]![QCDocAttributes]![AttributesDropdown] & "' ));"
.Source = strSQL
.LockType = adLockOptimistic
.CursorType = adOpenKeyset
.Open
End With
Set Me.Recordset = rs
Set rs = Nothing
Set cn = Nothing
End Sub

Getting ID of new record after insert

I'm just getting my head around insert statements today after getting sick of cheating with Dreamweaver's methods to do this for so long now (please don't laugh).
One thing I'm trying to figure out is how to get the ID value of a newly inserted record so I can redirect the user to that page if successful.
I have seen some examples which talk about stored procedures, but they're double dutch for me at the moment and I'm yet to learn about these, let alone how to use these from within my web pages.
Summary
How do I, using my code below retrieve the record ID for what the user has just inserted.
Workflow
Using a HTML form presented on an ASP page (add.asp), a user will submit new information which is inserted into a table of a database (treebay_transaction).
On pressing submit, the form data is passed to another page (add_sql.asp) which takes the submitted data along with additional information, and inserts it into the required table.
If the insert is successful, the id value of the new record (stored in the column treebay_transaction_id) needs to be extracted to use as part of a querystring before the user is redirected to a page showing the newly inserted record (view.asp?id=value).
Sample code - add_sql.asp
<!--#include virtual="/Connections/IntranetDB.asp" -->
...
<html>
<body>
<%
set conn = Server.CreateObject("ADODB.Connection")
conn.ConnectionString = MM_IntranetDB_STRING
conn.Open ConnectionString
...
sql="INSERT INTO treebay_transaction (treebay_transaction_seller,treebay_transaction_start_date,treebay_transaction_expiry_date,treebay_transaction_title,treebay_transaction_transaction_type,treebay_transaction_category,treebay_transaction_description,treebay_transaction_status)"
sql=sql & " VALUES "
sql=sql & "('" & CurrentUser & "',"
sql=sql & "'" & timestampCurrent & "',"
sql=sql & "'" & timestampExpiry & "',"
sql=sql & "'" & Request.Form("treebay_transaction_title") & "',"
sql=sql & "'" & Request.Form("treebay_transaction_transaction_type") & "',"
sql=sql & "'" & Request.Form("treebay_transaction_category") & "',"
sql=sql & "'" & Request.Form("xhtml1") & "',"
sql=sql & "'3')"
on error resume next
conn.Execute sql,recaffected
if err<>0 then
%>
<h1>Error!</h1>
<p>
...error text and diagnostics here...
</p>
<%
else
' this is where I should be figuring out what the new record ID is
recordID = ??
' the X below represents where that value should be going
Response.Redirect("index.asp?view.asp?id='" & recordID & "'")
end if
conn.close
%>
</body>
</html>
Run this after your execute statement and before you close your connection:
lsSQL = "SELECT ##IDENTITY AS NewID"
Set loRs = loConn.Execute(lsSQL)
llID = loRs.Fields("NewID").value
I pulled it from here:
http://www.kamath.com/tutorials/tut007_identity.asp
Build your sql variable as you have been. Let's make one trip to the DB instead of two. We'll use SCOPE_IDENTITY() right in the same statement(s) as the INSERT to avoid many trips to the database.
Add this when building your SQL statement:
sql=sql & "; SELECT SCOPE_IDENTITY() As NewTreebayTransactionID"
'now execute the insert and receive the ID in one Execute statement.
set newTransactionResults = conn.Execute(sql)
'here is our new ID.
recordID = newTransactionResults("NewTreebayTransactionID")
As soon as that's done:
sanitize your data inputs from your user
use parameterized statements
Set objConn = CreateObject("ADODB.Connection")
set rs = Server.CreateObject("ADODB.Recordset")
objConn.Open "DSN=connectionName"
rs.CursorLocation = 3
rs.Open "treebay_transaction", objConn, 3, 3
rs.AddNew fieldlist,values 'see link bellow to see how to fill this
rs.Update
bookmark = rs.absolutePosition ' First, store the location of you cursor
rs.Requery ' Next, update your recordset with the data from the database
rs.absolutePosition = bookmark ' Finally, change your cursor back
recordID = rs("ID")
rs.AddNew documentation: http://www.w3schools.com/ado/met_rs_addnew.asp
It depends on the database you are using, in SQL Server you can get the ##IDENTITY or SCOPE_IDENTITY() see: http://blog.sqlauthority.com/2007/03/25/sql-server-identity-vs-scope_identity-vs-ident_current-retrieve-last-inserted-identity-of-record/
But one thing I want to warn you, the code above has SEVERE security vulnerabilities, namely SQL Injection attack, please stay away from concatenating strings that are coming from users, you should use command paramaters.
look to ##IDENTITY, SCOPE_IDENTITY or IDENT_CURRENT
This makes the assumption that your ID field is an IDENTITY INSERT field. Also consider ramifications of the various options listed, as each one acts and performs slightly differently.
http://sqlserverpedia.com/wiki/Functions_-_##IDENTITY,_SCOPE_IDENTITY,_IDENT_CURRENT
With the same transaction :
Const adUseServer = 2
Const adOpenKeyset = 1
Const adLockOptimistic = 3
Set oConn = Server.CreateObject("ADODB.Connection")
oConn.Open "DSN=connectionName"
Set oRS = Server.CreateObject("ADODB.RecordSet")
oRS.CursorLocation = aduseserver
oRS.CursorType = adopenkeyset
oRS.LockType = adlockoptimistic
oRS.Open "treebay_transaction", oConn
if oRS.eof then
oRS.AddNew
oRS("treebay_transaction_seller") = CurrentUser
...
oRS.Update
recordID = oRS("treebay_transaction_id")
end if
oRS.Close
set oRS = nothing
oConn.Close
Set oConn = Nothing