Excel VBA workbook property for last modified vs SaveDate - vba

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.

Related

Save outlook excel attachments and concatenate the data

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!

Excel Generate New Workbook in a dynamic directory Path

I am using a workbook that generates reports according to the country selected. Each country uses an specific path directory.
When it comes to import information form their root folder its OK.
My problem is when I generate a new workbook with the report. I try to save it in the specific location which changes with the country:
'Generate a new workbook refering to the first Worksheet
Set WkReport = Workbooks.Add(xlWBATWorksheet)
With WkReport
// Skip selecting the sheet, just reference it explicitly and copy it after//
// the blank sheet in the new wb.
ThisWorkbook.Worksheets("REPORT").Copy after:=.Worksheets(.Worksheets.Count)
End With
// Kill alerts, delete the blank sheet in the new wb and turn alerts back on//
Application.DisplayAlerts = False
With WkReport
.SaveAs Filename:="L:\Fold1\Fold2\Fold3\" & rngFolder & "\" & rngYear & "\" & rngMonth &"\"& rngName & "_Report_" & rngDate & ".xlsx"
End With
Application.DisplayAlerts = True'`enter code here`
L:\Fold1\Fold2\Fold3: fixed path
rngFolder is the Path for the Country
rngYear is the Path for a subfolder within Country
rngMonth is the Path for a subfolder within the Year
(rngSmthing are ranges referring to cells in the workbook)
All those are dynamics ranges that changes according to information introduced by the user.
Therefore when I create the workbook it must be saved in different location according to this information.
Name of the file contains another dynamic range "rngName" followed up by Report and "rngDate":
Filename = rngName_Report_rngDate.xlsx
What my code does is to save in L:\Fold1\Fold2\Fold3 with the filename Report.xlsx
Examples of Path directories if user selects...
Germany:
L:Folder1\Folder2\Folder3\Germany\2015\06-2015\GE_Report_31-06-15.xlsx
Hungary:
L:Folder1\Folder2\Folder3\Hungary\2015\06_2015\HU_Report_31-06-15.xlsx
!PROBLEM SOLVED! I simply forgot to set the rngSmthng Variables... (Clap Clap) Anyway, someone may find it useful in case that you want to set different save paths according to your ranges:
'cellRef is a named cell within the workbook where user selects data
rngName = ws.Range("cellRef").Value
In that way you have a dynamic path finder.
Glad you found the answer. As a side-note - this is how I would write the procedure.
Sub Test()
Dim wkReport As Workbook
Dim sFolder As String
Dim sPath As String
Dim rngFolder As Range
Dim rngName As Range
With ThisWorkbook.Worksheets("Sheet1")
Set rngFolder = .Range("A1")
Set rngName = .Range("A2")
End With
sFolder = "L:\Fold1\Fold2\Fold3\" & rngFolder & "\" & Format(Date, "yyyy\\mm mmm\\")
CreateFolder sFolder
sPath = sFolder & rngName & "_Report_" & Format(Date, "dd-mm-yy") & ".xlsx"
Set wkReport = Workbooks.Add(xlWBATWorksheet)
With wkReport
ThisWorkbook.Worksheets("REPORT").Copy after:=.Worksheets(.Worksheets.Count)
.Worksheets(1).Delete
.SaveAs sPath, ThisWorkbook.FileFormat
End With
End Sub
' Purpose : Will Recursively Build A Directory Tree
Sub CreateFolder(Folder)
On Error Resume Next
Dim objFSO As Object: Set objFSO = CreateObject("Scripting.FileSystemObject")
If Folder <> "" Then
If Not objFSO.FileExists(objFSO.GetParentFolderName(Folder)) Then
Call CreateFolder(objFSO.GetParentFolderName(Folder))
End If
objFSO.CreateFolder (Folder)
End If
End Sub
Note:
Format(Date, "yyyy\\mm mmm\\") will return 2015\12 Dec\.
Format(Date, "yyyy\mm mmm\") will return 2015m12 Dec.
Really sorry guys...
And many thanks for your help... no way you could have guessed it.
The problem was that those variables I have them set in a different macro, which I completely forgot... so of course it does not recognize the variables.. because I didnt create them in this Macro!!
Again my apologies should review my code twice before posting

Auto generated save excel file name VBA Macro?

Auto generated excel file name VBA Macro?
Hi all i want auto generated excel file name in macro
my code is below here
Sub Sheet_SaveAs()
Dim wb As Workbook
Sheets("Sheet1").Copy
Set wb = ActiveWorkbook
With wb
.SaveAs ThisWorkbook.Path & "\autogenrate.xlsx"
'.Close False
End With
End Sub
my code is working fine but when i save next time then asking do you want replace it but i want auto generate name
The simplest fix is to change to a unique name each time. The easiest way to do this might be to use a date-time string
Sub Sheet_SaveAs()
Dim wb As Workbook
Sheets("Sheet1").Copy
Set wb = ActiveWorkbook
With wb
.SaveAs ThisWorkbook.Path & "\" & _
Format(Now, "yyyymmdd") & _
Replace(Format(Now, "Long Time"), ":", "") & _
".xlsx"
.Close False
End With
End Sub
The date and tie part are seperate to allow you to use seconds and therefore your limit is 1 save per second. If you need more frequent saves you would have to include a millisecond counter too. The good thing about this method is that it keeps your backups in sequential order in the folder.
You can read more about formatting dates etc. here - https://msdn.microsoft.com/en-us/library/office/gg251755.aspx

Rename Workbook with name of first worksheet VBA

I would like to rename each workbook in a folder as the name of the first worksheet in the workbook. For example, if the Workbook is called "71107" and the worksheet in that workbook is called "foobar", I would like the workbook to be renamed "foobar".
My current progress is as follows:
Sub RunMe()
Dim objFSO As New FileSystemObject
Dim objWkbk As Workbook
Dim objFile As File
Dim folderpath As String
Application.ScreenUpdating = False
folderpath = "D:\test\"
For Each objFile In objFSO.GetFolder(folderpath).Files
oldpath = objFile.path
Set objWkbk = Workbooks.Open(oldpath)
newpath = path & "NEWNAME\" & ActiveSheet.Name & ".xls"
objWkbk.SaveAs Filename:=newpath
objWkbk.Close
Next objFile
End Sub
This code runs extremely slowly (in the order of 10 seconds per file). My files are not large, only around 40kb each. Is there a faster way to perform this operation? I also don't need to save a new copy of each file, I'm quite happy to just rename the file I just don't know of a way to do that.
There are two things, which takes so long one is Open and SaveAs.
The Open can be avoided with ADO as mentioned and more details are here
Then you can also not use SaveAs because it is not opened but you can just rename the file using something like:
Name GivenLocation & OldFileName As GivenLocation & NewFileName

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.