MSAccess VBA to Save a Single Attachment to a Folder - ms-access

I'm really new to Access so I haven't heard of most of the commands for Access VBA, but I am pretty familiar with Excel VBA.
What I'm trying to do is save the attachment that was just entered into a table through a form. I've been looking at some examples online and trying to get it to work for me but the code is not moving the file to the folder. I do not get a debug error though.
Here is my current code. I know it is set to loop right now, where really I just want the last attachment in the table each time, but I don't know how to get only the last attachment. Either way, this current code doesn't move ANY attachments.
Private Sub cmdAddRecord_Click()
If MsgBox("Adding a new record will save the current form. You will not be able to edit this credit request. Would you like to continue?", vbQuestion + vbYesNo, "Save current record and open new form") = vbYes Then
MkDir "C:\Users\username\Desktop\IC Transfer Back Up Attachments\" & Me.txtRequestID & "-" & "Back Up Attachments" & " " & Format(Date, "MMDDYY")
DoCmd.RunCommand acCmdSaveRecord
Dim SaveFolder As String
SaveFolder = "C:\Users\username\Desktop\IC Transfer Back Up Attachments\" & Me.txtRequestID & "-" & "Back Up Attachments" & " " & Format(Date, "MMDDYY")
Dim rsParent As DAO.Recordset2
Dim rsChild As DAO.Recordset2
Set rsParent = CurrentDb.OpenRecordset("SELECT * FROM tblICTRequested")
Set rsChild = rsParent.Fields("BackUpAttachments").Value
Do Until rsChild.EOF
rsChild.Fields("FileData").SaveToFile SaveFolder
rsChild.MoveNext
Loop
DoCmd.RunCommand acCmdCloseWindow
DoCmd.OpenForm "frmICTRequested"
End If
End Sub
Most of this seems to make sense to me, but I'm not sure what I should put in the .Fields("FileData").SaveToFile line, since I don't have a field named "FileData" but I've tried all my existing fields to no avail.
For reference, here are some of the online links I have reviewed:
https://www.experts-exchange.com/questions/29005769/MS-Access-attachment-file.html
https://msdn.microsoft.com/en-us/library/office/ff191852.aspx
https://access-programmers.co.uk/forums/showthread.php?t=282135
Any tips? Much appreciated!

You're very close. I use a function like this:
Public Function SaveFileToDisk(FileName As String, FileData As DAO.Field2, Optional saveToFolder As String) As String
Dim templatePath As String
If saveToFolder = "" Or Not fso.FolderExists(saveToFolder) Then
saveToFolder = Environ("temp")
End If
templatePath = GetAvailableFileName(FileName, saveToFolder, True) 'A function to create a unique file name
FileData("FileData").SaveToFile templatePath
SaveTemplateToDisk = templatePath
End Function
It gets called like this:
Dim tempPath As String
Dim fileData as DAO.Field2
Dim folderToSaveTo as string
folderToSaveTo = "C:\some\folder"
set fileData = rsParent.Fields("BackUpAttachments")
tempPath = exporter.SaveTemplateToDisk("Name of file.ext", fileData , folderToSaveTo)
The attachment field is kind of like a recordset withing a field.

So with the help of someone, I changed the line:
Set rsParent = CurrentDB.OpenRecordset("SELECT * FROM tblICTRequested")
To:
Set rsParent = CurrentDB.OpenRecordset("SELECT * FROM tblICTRequested WHERE ID =" & Me.txtRequestedID)
This seems to be working perfectly for my purpose! Thank you to everyone who provided information!

Related

Display Pdf preview in Ms Access Report using pdf file path

I am new in MS Access. I have pdf file location in textbox. I want when access report load then specific pdf file preview in that report (pdf read from file location). How can I achieve it? Please help?
You can display PDF in Report by converting its pages to images and display them. Withwsh.Runyou can extract duringReport_Loadevent, then store the pages paths in a temporary table.
Have Irfanview with PDF-Plugin installed.
In Front-End, create a table namedTmpExtractedPageswith oneShort-Textfield namedPathto store the paths of the extracted pages.
Create a report with Record-Source.
SELECT TmpExtractedPages.Path FROM TmpExtractedPages;
Add a Picture-Control in Detail-Section (no Header/Footer-Section), that fits to the page and bind it toPath
Put the following code inReport_Loadevent
Private Sub Report_Load()
Dim TempPath As String
TempPath = CurrentProject.Path & "\TempPdf"
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FolderExists(TempPath) Then
fso.DeleteFolder TempPath
End If
fso.CreateFolder TempPath
Dim PdfFile As String
PdfFile = Me.OpenArgs
Const PathToIrfanView As String = "C:\Program Files (x86)\IrfanView\i_view32.exe"
Dim CmdArgs As String
CmdArgs = Chr(34) & PdfFile & Chr(34) & " /extract=(" & Chr(34) & TempPath & Chr(34) & ",jpg) /cmdexit" 'see i_options.txt in IrfanView folder for command line options
Dim ShellCmd As String
ShellCmd = Chr(34) & PathToIrfanView & Chr(34) & " " & CmdArgs
Debug.Print ShellCmd
Dim wsh As Object
Set wsh = CreateObject("WScript.Shell")
Const WaitOnReturn As Boolean = True
Const WindowStyle As Long = 0
wsh.Run ShellCmd, WindowStyle, WaitOnReturn
With CurrentDb
.Execute "Delete * From TmpExtractedPages", dbFailOnError
Dim f As Object
For Each f In fso.GetFolder(TempPath).Files
.Execute "Insert Into TmpExtractedPages (Path) Values ('" & Replace(f.Path, "'", "''") & "');", dbFailOnError
Next f
End With
Set fso = Nothing
Set wsh = Nothing
End Sub
You provide the path to the PDF to display asOpenArgsargument on open report:
DoCmd.OpenReport "rpt_pdf", acViewPreview, , , , "path\to\pdf"
Keep in mind that adding, then deleting records to the temp table, will bloat your database if you don't compact it later (or just deploy a fresh Front-End copy on start, as I do).
If you just need to display the pdf file, you could create a button next to the textbox and in its on click event:
Private Sub cmdView_Click()
If Nz(Me.txtPdfLocation) <> "" Then
Application.FollowHyperlink Me.txtPdfLocation
End If
End Sub

Splitting a report into separate emails with their individual reports

I am trying to send separate Employees a PDF/page of their section/report. The information is based on their EmployeeID (which is text not long number). So each person has their balance information on a page then there's a page break, and then next page shows the next person's details. With the code below, it does email each of the employees one page but it so happens to only email the first person's page to EVERYONE. Is it possible to somehow automate each week so that each user is emailed his/her individual page of the report?
Another error is that the email pop up one by one so I have to press send each time for over 200 people, and that the email seems to be sending to the email but then followed by #mailto:the email# for example email#email.com#mailto:email#email.com#
I just started Access and have been copying and scraping code off of places I have found online. Many thanks in advance, if you can assist!
Have a great day!
Private Sub cmdSendAll_Click()
Dim rsAccountNumber As DAO.Recordset
Dim strTo As Variant
Dim strSubject As String
Dim strMessageText As String
Set rsAccountNumber = CurrentDb.OpenRecordset("SELECT DISTINCT EmployeeID, [Email] FROM [queAutoUpdate]", dbOpenSnapshot)
Debug.Print strTo
With rsAccountNumber
Do Until .EOF
DoCmd.OpenReport "test", _
acViewPreview, _
WhereCondition:="EmployeeID = '" & !EmployeeID & "'", _
WindowMode:=acHidden
strTo = ![Email]
strSubject = "Updated Balance "
strMessageText = "Text Here"
DoCmd.SendObject ObjectType:=acSendReport, _
ObjectName:="test", _
OutputFormat:=acFormatPDF, _
To:=strTo, _
Subject:=strSubject, _
MESSAGETEXT:=strMessageText, _
EditMessage:=True
DoCmd.Close acReport, "Unaffirmed Report", acSaveNo
.MoveNext
Loop
.Close
End With
End Sub
Your opening a report called test and then closing another report called "Unaffirmed Report". You need to open and close the same report, in this case "test".
DoCmd.Close acReport, "test", acSaveNo. This should fix the employee data not updating, since the report remains open on the first employee.
To directly send the message you need change EditMessage:=True to EditMessage:=False.
Check the docs:
https://learn.microsoft.com/en-us/office/vba/api/access.docmd.sendobject
Also if you need to test this, set outlook in Offline mode, and run your code, check the messages in your Outbox to see if they're as expected. You can delete the messages from the Outbox to prevent them from being sent. Once you're finished with testing you can set Outlook back to Online Mode.
Regarding the email address issue, this comes automatically when using hyperlinks in your controls. You'll need to strip the extra part out with strTo = Left(![Email],InStr(![Email],"#")-1). Check your data if this will be valid for all email addresses. For a more advanced solution you can look at this post https://codekabinett.com/rdumps.php?Lang=2&targetDoc=access-hyperlink-data-type.
Code provided as reference, please see the post for the explanation.
'copied from https://codekabinett.com/rdumps.php?Lang=2&targetDoc=access-hyperlink-data-type
Public Function GetHyperlinkFullAddress(ByVal hyperlinkData As Variant, Optional ByVal removeMailto As Boolean) As Variant
Const SEPARATOR As String = "#"
Dim retVal As Variant
Dim tmpArr As Variant
If IsNull(hyperlinkData) Then
retVal = hyperlinkData
Else
If InStr(hyperlinkData, SEPARATOR) > 0 Then
' I append 4 separators at the end, so I don't have to worry about the
' lenght of the array returned by Split()
hyperlinkData = hyperlinkData & String(4, SEPARATOR)
tmpArr = Split(hyperlinkData, SEPARATOR)
If Len(tmpArr(1)) > 0 Then
retVal = tmpArr(1)
If Len(tmpArr(2)) > 0 Then
retVal = retVal & "#" & tmpArr(2)
End If
End If
Else
retVal = hyperlinkData
End If
If Left(retVal, 7) = "mailto:" Then
retVal = Mid(retVal, 8)
End If
End If
GetHyperlinkFullAddress = retVal
End Function
Consider using the MS Outlook object library to send emails. Whereas DoCmd.SendObject is a convenience handler, you control more of the process with initializing an Outlook application object and creating an Outlook email object setting all needed elements.
However, with this approach you need to first export your filtered report to PDF and then attach to email for final send. See inline comments for specific details.
Dim rsAccountNumber As DAO.Recordset
' CHECK Microsoft Outlook #.# Object Library UNDER Tools/References
Dim olApp As Outlook.Application, olEmail As Outlook.MailItem
Dim fileName As string, todayDate As String, strEmail As String
todayDate = Format(Date, "YYYY-MM-DD")
Set rsAccountNumber = CurrentDb.OpenRecordset("SELECT DISTINCT EmployeeID, [Email] FROM [queAutoUpdate]", dbOpenSnapshot)
Set olApp = New Outlook.Application
With rsAccountNumber
Do Until .EOF
' SETTING FILE NAME TO SAME PATH AS DATABASE (ADJUST AS NEEDED)
fileName = Application.CurrentProject.Path & "\Balance_Report_" & !EmployeeID & "_" & todayDate & ".pdf"
' OPEN AND EXPORT PDF TO FILE
DoCmd.OpenReport "test", acViewPreview, "EmployeeID = '" & !EmployeeID & "'"
' INTENTIONALLY LEAVE REPORT NAME BLANK FOR ABOVE FILTERED REPORT
DoCmd.OutputTo acReport, , acFormatPDF, fileName, False
DoCmd.Close acReport, "test"
' CREATE EMAIL OBJECT
strEmail = ![Email]
Set olEmail = olApp.CreateItem(olMailItem)
With olEmail
.Recipients.Add strEmail
.Subject = "Updated Balance"
.Body = "Text Here"
.Attachments.Add fileName ' ATTACH PDF REPORT
.Send ' SEND WITHOUT DISPLAY TO SCREEN
End With
Set olEmail = Nothing
.MoveNext
Loop
.Close
End With
MsgBox "All emails successfully sent!", vbInformation, "EMAIL STATUS"
Set rsAccountNumber = Nothing: Set olApp = Nothing

Importing a daily file with variable name into access database

Let me apologize in advance if this question has been posed somewhere and I overlooked it. I've spent multiple days on this and cannot get it to run 100%.
I am trying to import an excel file that gets sent via email every morning into an access database. The file has a date portion that changes every day. The naming follows the same pattern everyday of "FTTQ m-dd-yyyy". The day shown in the file name is for the previous work day, ex. receive email on 8/25 for FTTQ on 8/24. The code below is what I have so far and it will loop through the folder, however when it reaches the correct day it cannot find it. I have tried a couple variations but Access keeps crashing when I try to run it. Ideally I need Access to find the latest date on the file and import it, such as coming in on Monday and getting the file for Friday/Saturday or during the week getting it for the day before. Any help will be greatly appreciated.
Private Sub Button1_Click()
Dim strToday As String
Dim strFilePath as String
Dim strFile as String
strToday = Format(Date, "m-dd-yyyy")
strFilePath = "C:\Users\cole.stratton\Documents\Procurement\FTTQ 'Note:FTTQ is the beginning of the file name
strFile = Dir(strFilePath, "*.xlsx")
Do While strFile <> ""
If Right(strFile,14) = strToday & ".xlsx" Then
DoCmd.TransferSpreadsheet, acImport, "tblTest",strFile, True
End If
strFile = Dir 'Note: I do not understand the point of this line or what it does or supposed to do.
Loop
End Sub
To find the latest existing file, I would change the loop like this:
Dim searchDate As Date
Dim strDate As String
Dim strFilePath As String
Dim strFile As String
Dim i As Long
' Search backwards from today for a file with the date name
For i = 0 To -7 Step -1
searchDate = DateAdd("d", i, Date)
strDate = Format(searchDate, "m-dd-yyyy")
strFilePath = "C:\Users\cole.stratton\Documents\Procurement\FTTQ " & strDate & ".xlsx"
Debug.Print "Looking for: " & strFilePath
' Check if file exists
strFile = Dir(strFilePath)
If strFile <> "" Then
' Note that Dir() only returns the file name, so use strFilePath
DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel12, "tblTest", strFilePath, True
' file found, exit loop
Exit For
End If
Next i
***I'm assuming that you have the closing " in your strFilePath line in your actual code. ****
This line looks like the issue...
strFile = Dir(strFilePath, "*.xlsx")
This page will show you the correct syntax for using Dir...http://www.techonthenet.com/excel/formulas/dir.php
strFile = Dir(strFilePath & "*.xlsx") <-- you were putting the file extension in where the attributes were supposed to go.
However, you also need to change your date. If the file will have yesterday's date, not today's...strToday = Format(Date-1, "m-dd-yyyy")
This line...
strFile = Dir
sets your string to the next file name that meets your search criteria.

Searching For a file name inside a table based on the list double click event

Hello Fellow programmers,
I am absolutely desperate as I can not figure out how to solve (maybe) simple problem. I have two tables. First one [Files] with two fields: [FName](file name) and [FPath](file path) and second one [Reports] with [DocNo] [Title]...blah blah...
FName string consists of [DocNo] [Title](but the whole title string is not as a file path)
Example:
[DocNo] Smith/RT/2000/001
[Title] Assessment of modified aluminothermic welds in 68kg/m head hardened rail for BHP Iron Ore Pty Ltd
[FName] SmithRT2000001 Assessment of modified aluminothermic welds .pdf
I have a form which has a search list on it. this list brings up records which are in [Reports]. By double clicking on a specific record, it fires up doubleclick event. in the Event I get the value of DocNo and Title and search into Files table for the Fname to match. But surprisingly it doesn't return anything when I put the sql search or even in the design mode for query?
BUT the funny thing is that when I hard code to find the record, both of ways will find it. how is that?
Here is the VBA to check out:
Private Sub SearchResults_DblClick(Cancel As Integer)
'Initializing the string variables
Dim strSQL As String
Dim strFileName As String
Dim strTitle As String
Dim DocumentNo As String
Dim titleLeng As Integer
DocumentNo = Me.SearchResults.Column(0)
DocumentNo = Replace(DocumentNo, "/", "")
strTitle = Me.SearchResults.Column(1)
Debug.Print (DocumentNo)
SrchText = DocumentNo
SearchResults.RowSourceType = "Table/Query"
SearchResults.RowSource = "QRY_OpenFile"
Debug.Print (strTitle)
strTitle = Left(strTitle, 10)
SrchText = strTitle
Debug.Print (SrchText)
SearchResults.RowSource = "QRY_OpenFile"
Dim rst As Recordset
Dim db As DAO.Database
Set db = CurrentDb()
strSQL = "SELECT Files.FName FROM Files WHERE Files.FName Like " * " & strTitle & " * ";"
Debug.Print (strSQL)
Set rst = db.OpenRecordset(strSQL, dbOpenDynaset)
Application.FollowHyperlink strFileName, , True, False, , , "John Smith"
I have tried every variation in SQL string, changing outer " " to ' ' does not work. But if I change strTitle with "Assessment" string or "SmithRT2000001" it will finds it. DO not know why?
This does not work in the query design window where you put criteria:
Like "* & Forms![Search For Reports]![SrchText] & *"
But as soon as I change it something static it will work. Going crazy!!
Can you guide me as what to do or how to achieve my goal which is opening the file in FILE table??
Okay, After testing 3 different approaches at the end one of them gave a good response and what I wanted. I changed the "like" command in my query to:
Like "*" & [Forms]![Search For Reports]![SrchText] & "*"
and suddenly it worked. Also I found out that SQL Select query doesn't work from VBA specially with Double click event.
Here is the final code:
Private Sub SearchResults_DblClick(Cancel As Integer)
'Initializing the string variables
Dim strTitle As String
Dim DocumentNo As String
DocumentNo = Me.SearchResults.Column(0)
DocumentNo = Replace(DocumentNo, "/", "")
strTitle = Me.SearchResults.Column(1)
strTitle = Replace(strTitle, "'", "''")
SrchText.Value = DocumentNo
SearchResults.RowSourceType = "Table/Query"
SearchResults.RowSource = "QRY_OpenFile"
End Sub
I could not get the path and name from the list, to put them together and fire up a hyperlink to Acrobat...What I had to do was sending an event via a button to get the values from the list. For some reason after SearchResults.RowSource = "QRY_OpenFile" the list.Column(index) was returning null.
Anyway thanks for reading my question and thinking about it.
Did that code actually run? You have the SQL string in a tangle:
''You need to watch out for quotes in the string, so
strTitle = Replace(strtile, "'", "''")
strSQL = "SELECT Files.FName FROM Files WHERE Files.FName Like '*" _
& strTitle & "*';"
The point of this line:
Debug.Print (strSQL)
Is to get an SQL string to test in the query design window. Use it.

Copying a record in VBA 2 (the new question)

I have this code that I am trying to copy a record with in VBA. Unfortunately I cannot get it to work. I do not knw why it won't do anything.
Dim OldRecord As DAO.Recordsets, NewRecord As DAO.Recordset
Dim strSQL As String
strSQL = "SELECT [Tote Log].* FROM [Tote Log] WHERE Number = " & _
Me.tbScannerRead.Value
Set OldRecord = CurrentDb.OpenRecordset(strSQL)
If OldRecord.Count = 1 Then
Set NewRecord = _
CurrentDb.OpenRecordset("SELECT [Tote Log].* FROM [Tote Log]")
With NewRecord
.AddNew
For i = 0 To .Fields.Count - 1
.Fields(i).Value = OldRecord.Fields(i).Value
Next i
.Fields("Number").Value = Me.tbScannerRead & "B2"
.Update
varBookMark = .Bookmark
End With
NewRecord = varBookMark
DoCmd.RunCommand acCmdSelectRecord
Me.tbMessageBox = "Added new record"
Me.tbMessageBox.Visible = True
GoodToteRead = False
Me.tbScannerRead.SetFocus
End If
I get nothing, I am trying to copying a record from the tote log and change the number from, lets say, L20444 to L20444B2 and have the same field information as the original. This is where I am so far but I get nothing. Ahy Help would be greatly, and I mean greatly, appreciated. Thanks
There are a few things that could be causing it. Here is one. Does your table have a primary key? It looks like you are trying to update the primary key to a value that already exists in the table before changing it. Is this happening on a form? If so Access can get upset at you for changing a recordset behind it's back. a me.undo() before making changes can help. Also if you are on a form you can acomplish the same thing this way. It's a bit hacky, but it is the easy way.
DoCmd.RunCommand acCmdSelectRecord
DoCmd.RunCommand acCmdCopy
DoCmd.GoToRecord , , acNewRec
DoCmd.RunCommand acCmdPaste
As an alternative, I would recommend something along these lines.
Dim sSql As String
Dim sUpdateSuffix as string
sUpdateSuffix="B2"
Const sTableName As String = "[Tote Log] "
sSql = "INSERT INTO "[Tote Log]([number],[whateverelse]) " & _
"SELECT [number]" & sUpdateSuffix & ",[whateverelse] FROM [Tote Log] WHERE Number = " & Me.tbScannerRead.Value
CurrentProject.Connection.Execute sSql
If you want to build the sql string dynamically use the same method as you already used to loop through the fields and build the query string.
me.requery will rebuild the form recordset.
Hope that helps you
Well it might actually be saving the database record but not redisplaying it; I'm having a hard time deciphering that part of the code, and I don't know what your form is bound to.
Anyway, you should open your recordsets like this:
Set rs = db.OpenRecordset(strSQL, dbOpenDynaset, dbSeeChanges)
especially if you are using SQL Server as the backend (which you should).
Once you have saved the record, you should probably just reload the record back into your form by doing a recordset.find(), rather than trying to bookmark it. Bookmarks only work on the same recordset they originated from. This provides round-trip verification that the data was actually saved into the database.