Save outlook excel attachments and concatenate the data - vba

I have a requirement that I am doing everyday and spending around 3 hours to do that. I would like it to be automated so that I can save time.
Every day I will get an email which contains around 100 attachments in *.msg format (Attachments are actually mails from different vendors).
In the *.msg files there will be one Excel file and this Excel file contains one line of data.
What I would like to do is Extract the first email attachments (which is in .Msg format) and extract the Excel file in each .Msg attachment and then copy the data from the XLS files into a new Excel file (Consolidating the data from each Excel attachment for calculations)!!
Can some one please provide a way to automate this process in vba macro and I would require detailed step as I am a newbie into the coding area.

This is as close as I can get to automating this using VBA. It's a semi-automatic solution that will require you to save the .msg attachments in a folder first and then open them all manually (i.e. select all, right-click one and chose Open). I tested this on 98 emails being opened at once, my Outlook slowed down a bit towards the end but it did not crash. The code itself takes under 3 minutes to complete the task.
But first, some assumptions were made based on the info provided:
All the .msg attachments are in one email message - if they are not, you may need to repeat step 1 as many times as necessary.
The Excel files inside the .msg files are in ".xls" format. If they are in ".xlsx" or other Excel format simply modify the relevant lines of code under Step 3.
You stated that the Excel files contain just 1 row of data - the below assumes it's row 1 in sheet 1. If not, the code will need to be adjusted slightly before it works.
Also, the solution ignores cell formatting in the Excel files - only the values from cells will be copied - this can be modified if needed.
You have some understanding of VBA, know how to insert module, compile, run and edit code in VB Editor - if not, just post your questions in a comment below, I'll be happy to help further.
The solution:
Step 1 - Save all the ".msg" attachments to a folder.
Step 2 - Select all emails in the folder and open them - this should open multiple Outlook inspector windows.
Step 3 - In Excel, activate VB Editor and paste the below code in a module:
Sub GetAttachments()
'loops through Outlook inspector windows extracting .xls attachments into a folder
Dim oShell As Object
Dim olApp As Object
Dim Insp As Object
Dim Att As Object
Dim FldPth As String
Dim myFname As String
Dim i As Long
Set olApp = CreateObject("Outlook.Application")
Set oShell = CreateObject("Shell.Application").BrowseForFolder(0, "Select Folder with attachments", 0)
If oShell Is Nothing Then MsgBox "Folder was not selected", vbCritical: Exit Sub
FldPth = oShell.self.Path
'loop through open outlook windows (inspectors)
'use reversed loop, otherwise every second iteration will be skipped
For i = olApp.Inspectors.Count To 1 Step -1
Set Insp = olApp.Inspectors.Item(i)
'loop through attachments in the email message
For Each Att In Insp.CurrentItem.Attachments
myFname = Att.Filename
'if the attached file is an xls type, save it in a folder
If LCase(Right(myFname, 4)) = ".xls" Then
Att.SaveAsFile FldPth & "\" & myFname
End If
Next Att
'close the inspector window
Insp.Close olDiscard
Next i
Set oShell = Nothing
Set olApp = Nothing
MsgBox "Done!"
End Sub
Sub GetDataFromWbks()
'loops through Excel files in selected folder extracting data from first row in sheet 1 into active worksheet
Dim oShell As Object
Dim FSO As Object
Dim f As Object
Dim srcWbk As Workbook
Dim dstWs As Worksheet
Dim srcRng As Range
Dim dstRng As Range
Dim FldPth As String
Dim i As Long
Set oShell = CreateObject("Shell.Application").BrowseForFolder(0, "Select Folder with attachments", 0)
If oShell Is Nothing Then MsgBox "Folder was not selected", vbCritical: Exit Sub
FldPth = oShell.self.Path
Set dstWs = ActiveSheet
Set dstRng = dstWs.Rows(1)
Set FSO = CreateObject("Scripting.FileSystemObject")
Application.ScreenUpdating = False
'loop through all files in folder
For Each f In FSO.GetFolder(FldPth).Files
If LCase(Right(f.Name, 4)) = ".xls" Then
'show progress in Excel's status bar
Application.StatusBar = i
'open xls file
Set srcWbk = Workbooks.Open(f.Path)
'set source range
Set srcRng = srcWbk.Sheets(1).UsedRange.Rows(1)
'copy source range to destination range
dstWs.Range(srcRng.Address).Offset(i).Value = srcRng.Value
i = i + 1
'close workbook
srcWbk.Close
End If
Next f
Application.ScreenUpdating = True
Application.StatusBar = False
Set FSO = Nothing
Set oShell = Nothing
MsgBox "Done!"
End Sub
Step 4 - Run the "GetAttachments" procedure. When prompted, select the folder you saved your attachments in. The procedure should extract all the Excel attachments from .msg files into the same folder.
Step 5 - Once done (PopUp box showed up), activate the Excel worksheet you want your data to be copied into (I suggest you use a new empty worksheet here) and run the "GetDataFromWbks" procedure - again, select your folder when prompted. You can watch the progress in Excel's status bar.
Step 6 - That's it, enjoy the saved time!

Related

Excel VBA workbook property for last modified vs SaveDate

I am noticing users modifying excel workbooks but the date for the SaveDate is not being updated. We would like to know when the last updated their workbooks.
As far as I can tell the
Workbook.BuiltinDocumentProperties collection does not include a property for ModifiedDate. Is there some way we can tell our users to save so that the SaveDate is updated?
Or can we actually get the Modified date from VBA?
I don't believe that there is a native function to get a file's Date Modified date, but you can pretty easily use the windows scripting reference to FileSystemObject to get the File.DateLastModified property.
I'm not completely sure how you'd like to implement this, but here are two examples you could consider:
When the following example is triggered, it reaches the code to prompt the user if the ActiveWorkbook has unsaved changes and if the last save was longer than 15 minutes ago.
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Dim wb As Workbook
Set wb = ActiveWorkbook
If DateDiff("n", fso.getfile(wb.FullName).DateLastModified, Now) > 15 And wb.Saved = False Then
'prompt user to save or save for them
End If
If there are potentially multiple workbooks open then you might want to use the next example which loops through all open workbooks before completing the same operation as above.
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Dim wb As Workbook
For Each wb In Application.Workbooks
Dim wbFile As Object
Set wbFile = fso.getfile(wb.FullName)
Debug.Print "filename: " & wbFile.name & vbCrLf & _
"modified: " & wbFile.DateLastModified & vbCrLf
If DateDiff("n", wbFile.DateLastModified, Now) > 15 And wb.Saved = False Then
'prompt user to save or save for them
End If
Next wb
If for some reason you instead have to loop through a directory to look for files, this site gives a nice example on how to do so.

Excel VBA Macro to match cell value from workbooks in a root folder then copy specific cell

Picture above is the master workbook. Can anyone help me to write the vba so it will find workbooks throughout the root folder (e.g. C:\Work\2017) that match with the account number and copy the B9 and E9 cells to the master cell. The 2nd picture is a system generated workbook with random name (e.g. export!-097a0sdk.xls), that's why I need a shortcut to make this task easier.
This is the result I expected by using the code
This is the excel generated by system
Thank you
If I understood correctly then the following will loop through a given directory and it will open and check each file for the required information, if found, it will add the values to your Master workbook.
Note: This code will not open a file if it has "Master" in its filename.
Sub LoopThroughFolder()
Dim FSO As New FileSystemObject
Dim myFolder As Folder
Dim wb As Workbook
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim myFile As File
Dim AccNumber As String
Dim LastRow As Long, i As Long
Dim sPath As String
sPath = "C:\Work\2017"
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
'get the last row with data on Column A
Application.DisplayAlerts = False
'do not display alerts
Set myFolder = FSO.GetFolder(sPath) 'set the root folder
For Each myFile In myFolder.Files 'for each file in the folder
If InStr(myFile.Name, "Master") = 0 Then
'if file to open does not have "Master" in it's name then
Set wb = Workbooks.Open(myFile.Path) 'open the file
AccNumber = wb.Sheets(1).Range("B2") 'check for account number on first Sheet
For i = 1 To LastRow 'loop through current Sheet to check if we have a match for the account number
If ws.Cells(i, 1) = AccNumber Then 'if match
ws.Cells(i, 2) = wb.Sheets(1).Range("B9") 'pass the values from the required range
ws.Cells(i, 3) = wb.Sheets(1).Range("E9")
End If
Next i
wb.Close False 'close and do not save changes
Set wb = Nothing
End If
Next
Application.DisplayAlerts = True
End Sub
Also you might have to set a reference to the relevant library to use FileSystemObject, to do that:
How do I use FileSystemObject in VBA?
Within Excel you need to set a reference to the VB script run-time library.
The relevant file is usually located at \Windows\System32\scrrun.dll
To reference this file, load the
Visual Basic Editor (ALT+F11)
Select Tools > References from the drop-down menu
A listbox of available references will be displayed
Tick the check-box next to 'Microsoft Scripting Runtime'
The full name and path of the scrrun.dll file will be displayed below the listbox
Click on the OK button.

Automating the Process of Renaming the Column Names in Multiple Excel Sheets

I have a requirement from the client in which they want us to rename the column from multiple excel sheets which are present in the same directory.
There are 70+ excel reports and we are not sure that the particular column is present in which excel file. So every time they ask us to change, all the time we have to drill down all the excel sheets to find out the changes required which is really time consuming.
I thought of automating the process. Since all the files are present under the same directory, can't we use a MACRO,BATCH/UNIX SCRIPTS or any other way by which we can traverse the entire directory and make those changes by performing a find and replace thing.
So my first question is, if this is feasible ? If yes, then can anyone suggest/advice how to work around on this process ?
Thanks in Advance
I can't take full credit for the below as this is a patchwork of code I have used over the years. This is how I would go about it personally:
Manually make a copy of the files to change and place them in a folder (keep the originals safe!)
Let the code open each file and change it
Code will save a copy in a different 'done' folder
The example below loops through each Excel file and moves it from the 'to-do' folder to the 'done' folder once it has changed the cell "A1" to "Hello World". When the 'to-do' folder is empty the code stops.
You'll need to change the file paths for this to work.
Sub Example()
Dim FolderPath As String, FilePath As String, FileCount As Integer
Dim objExcelApp As Object
Dim wb As Object
Dim SaveName As String
FolderPath = "C:\Users\********\Desktop\To Do\"
NewFolderPath = "C:\Users\********\Desktop\Done\"
FilePath = FolderPath & "*.xl??"
FileName = Dir(FilePath)
ChangeNextFile:
FileCount = 0
'count how many files in "files to be changed" folder
Do While FileName <> ""
FileCount = FileCount + 1
FileName = Dir()
Loop
'if there are no files left end the code
If FileCount = 0 Then GoTo FinishedLoadingFiles
'choose the first file to change
FileName = Dir(FilePath)
Debug.Print FileName
'create an instance of Excel
Set objExcelApp = CreateObject("Excel.Application")
With objExcelApp
.Visible = False
.DisplayAlerts = False
End With
'opens the excel file in the background
objExcelApp.Workbooks.Open FileName:=FolderPath & FileName, UpdateLinks:=False
Set wb = objExcelApp.ActiveWorkbook
objExcelApp.ActiveWindow.Activate
'changes cell "A1" to say "hellow world"
wb.Sheets(1).Cells(1, 1).Value = "Hello World"
'saves the file in the done pile
wb.saveas NewFolderPath & FileName '& ".xlsb"
'closes excel
With objExcelApp
.DisplayAlerts = True
End With
wb.Close
objExcelApp.Quit
Set wb = Nothing
Set objExcelApp = Nothing
'deletes the original file. New file has been saved in the new folder
Kill FolderPath & FileName
GoTo ChangeNextFile
FinishedLoadingFiles:
MsgBox "All done"
End Sub

VBA to Copy Contents from Embedded Word document and retain formatting

I'm using Excel 2010 with an embedded Word Document. The Word document is basically a communication template with some formatting (bold / underline / hyperlinks).
Process: User opens Excel Document, provides inputs to Excel, and completes template. There is no interaction between the inputs in Excel and the contents of the template.
I'm trying to build out this process such that once they edit the embedded Word Document the user hits a button. The VBA code would then take the contents of the embedded Word document and paste (formatting and all) it as the body of the email. The file would attach itself to that email, and off it would go for approval.
I've been able to locate code to get me part of the way there, and to give props where props are due, I located the code here (see below for the code)
But this doesn't retain the Word Document's Formatting. Any recommendations? Maybe if I could extract the Word contents as HTML that would work. But not sure how to do that. All help appreciated.
Sub Test()
Dim Oo As OLEObject
Dim wDoc As Object 'Word.Document
'Search for the embedded Word document
For Each Oo In Sheet8.OLEObjects
If InStr(1, Oo.progID, "Word.Document", vbTextCompare) > 0 Then
'Open the embedded document
Oo.Verb xlVerbPrimary
'Get the document inside
Set wDoc = Oo.Object
'Copy the contents to cell A1
wDoc.Content.Copy
With Sheet8.Range("M1")
.PasteSpecial xlPasteFormats
.PasteSpecial xlPasteValues
End With
'Select any cell to close the document
Sheet8.Range("M1").Select
'Done
Exit For
End If
Next
Set wDoc = Nothing
End Sub
After reviewing comintern's code, I was getting an error I couldn't solve for. I went back to the boards and located some additional code. Merging the two seems to have fixed it.
Sub HTMLExport()
Dim objOnSheet As oleObject
Dim strFileName As String
Dim sh As Shape
Dim objWord As Object ''Word.Document
Dim objOLE As oleObject
Sheet8.Activate
Set sh = ActiveSheet.Shapes("RA_Template")
sh.OLEFormat.Activate
Set objOLE = sh.OLEFormat.Object
Set objWord = objOLE.Object
ActiveSheet.Range("M1").Activate
''Easy enough
strFileName = CreateObject("Scripting.FileSystemObject").GetSpecialFolder(2) & "\temp.html"
objWord.SaveAs2 Filename:=strFileName, FileFormat:=10 '10=wdFormatFilteredHTML
'Copy the file contents into cell M1...
Dim handle As Integer
handle = FreeFile
Open strFileName For Input As handle
Sheet8.Range("M1").Value = Input$(LOF(handle), handle)
Close handle
'Delete the Temp File (strFileName)
Kill strFileName
'Select any cell to close the document
Sheet8.Range("M1").Select
End Sub`
If getting it into HTML will work (Word makes pretty ugly HTML...), you can just save it to a temp file and then pick it back up:
Sub HTMLExport()
Dim Oo As OLEObject
'Search for the embedded Word document
For Each Oo In Sheet8.OLEObjects
If InStr(1, Oo.progID, "Word.Document", vbTextCompare) > 0 Then
Dim temp As String
'GetSpecialFolder(2) gives the user's temp folder.
temp = CreateObject("Scripting.FileSystemObject").GetSpecialFolder(2) & "\temp.html"
'10 = wdFormatFilteredHTML
Oo.Object.SaveAs2 temp, 10
'Copy the file contents into cell M1...
Dim handle As Integer
handle = FreeFile
Open temp For Input As handle
Sheet8.Range("M1").Value = Input$(LOF(handle), handle)
Close handle
'...and delete the temp file.
Kill temp
'Select any cell to close the document
Sheet8.Range("M1").Select
'Done
Exit For
End If
Next
End Sub
Note that if you're using this method, you can't open the embedded Word document after you get the OLEObject or Word won't allow you to save it.

Opening .xlsx with VBA, File in Use error. Read-only not working

I am attempting to scan through a specific folder and open the most recently titled Excel file. The files are named '10 1 13' and '10 2 13' ect. My sub correctly identifies the most recent file. However, when it attempts to open it, I get the following error:
'Filename' is currently in use. Try again later.
The file will usually be in use by someone, but is only modified once a day. All I need to do is open a read-only workbook and copy data from it. No modifying or saving is required, which is why I tried the 'ReadOnly:= True' arguement, but I still get an error message.
The file path '\Hsrkdfs\hsdata\rk\grp06....' is because I am pulling from a network where everyone's network access isn't mapped the same. Some access this folder from the G: drive, others the R:, and this macro must be functional from all computers. Debug points me to the 'Workbooks.Open Filename:=strFilename, ReadOnly:=True' line.
Is there a more robust way to open the Workbook? Another method to force it open regardless of use? Or a way to avoid the conflict entirely? Thank you.
Sub GetMostRecentFile()
Dim FileSys As FileSystemObject
Dim objFile As File
Dim myFolder
Dim strFilename As String
Dim dteFile As Date
'set path for files - CHANGE FOR YOUR APPROPRIATE FOLDER
Const myDir As String = "\\Hsrkdfs\hsdata\rk\grp06\Rockford Repair Station Quality\DELIVERY\Daily Status report - commercial"
'set up filesys objects
Set FileSys = New FileSystemObject
Set myFolder = FileSys.GetFolder(myDir)
'loop through each file and get date last modified. If largest date then store Filename
dteFile = DateSerial(1900, 1, 1)
For Each objFile In myFolder.Files
If objFile.DateLastModified > dteFile Then
dteFile = objFile.DateLastModified
strFilename = objFile.Path
End If
Next objFile
Workbooks.Open Filename:=strFilename, ReadOnly:=True
Set FileSys = Nothing
Set myFolder = Nothing
End Sub
Try using GetObject if you only need to read the file. Something like this:
Dim wb As Workbook
Set wb = GetObject(strFilename)
'Change this line to reflect the ranges you need to copy/paste.
wb.Sheets("Sheet1").Range("A1").Copy ThisWorkbook.Sheets("Sheet1").Range("A1")
wb.Close
Using this should allow you to copy from the workbook whether it's open by another user or not (including you).
I have noticed that this approach doesn't work if the workbook is protected or if the sheet you're trying to copy from is protected.
Also, only use ThisWorkbook like I did above if the code will be in the same workbook as the sheet you want to paste to.