Track changes in excel against previous version vba - vba

I am trying to track the changes made in an excel file used by multiple people. So far I have built the model (with help from stackoverflow) to save a version of itself within activeworkbook.path & "\Version Control".
The copy is saved when file opens and is named: date & time & activeworkbook.name
Currently it look at a range to determine if intersect then return the new value in sheet x cell y. How do I write the code to allow it to open the latest saved backup and give me in sheet x cell z the older versions value?
Code I have:
Track Changes:
Private Sub Worksheet_Change(ByVal Target As Range)
If Sheets("UserLog").Cells(1, 24) = IsBlank Then Exit Sub
Dim strAddress As String
Dim val
Dim dtmTime As Date
Dim Rw As Long
If Intersect(Target, Range("CheckRange5")) Is Nothing Then Exit Sub
dtmTime = Format(Now(), "YYYY-MM-DD HH:MM:SS")
val = Target.Value
strAddress = Target.Address
Rw = Sheets("UserLog").Range("D" & Rows.Count).End(xlUp).Row + 1
With Sheets("UserLog")
.Cells(Rw, 4) = strAddress
.Cells(Rw, 5) = val
.Cells(Rw, 6) = dtmTime
.Cells(Rw, 7) = Environ("Username")
.Cells(Rw, 8) = "SCI - SDBD"
End With
ActiveWorkbook.Save
End Sub
Saving the File:
Private Sub Workbook_Open()
Dim MyDate
MyDate = Date
Dim MyTime
MyTime = Time
Dim TestStr As String
TestStr = Format(MyTime, "hhmmss")
Dim Test1Str As String
Test1Str = Format(MyDate, "YYYY-MM-DD")
xxPath = ActiveWorkbook.Path
Application.DisplayAlerts = False
ActiveWorkbook.SaveCopyAs Filename:=xxPath & "\Version Control" & "\" & Test1Str & " " & TestStr & " " & ActiveWorkbook.Name
ActiveWorkbook.Save
Application.DisplayAlerts = True

Related

Save a number of excel worksheets as a PDF

Option Explicit
Dim mySheets As Dictionary
Private Sub SaveAndOpen_Click()
'set up variables
Dim i As Long
Dim j As Long
Dim myArr() As Long
Dim filename As String
ReDim myArr(1 To Sheets.Count)
j = 1
'make bounds
Dim from As Long
Dim tonum As Long
'numbers inputted from a userform
from = FromBox.Value
tonum = ToBox.Value
filename = Cells(3, 4) & "." & mySheets.Item(from) & "-" & mySheets.Item(tonum)
For i = 1 To mySheets.Count
If i >= FromBox.Value And i <= ToBox.Value Then
myArr(j) = i
j = j + 1
End If
Next i
Dim filepath As String
For i = 1 To UBound(myArr)
filepath = filepath & myArr(i)
Next i
filepath = "c:\file\path\here\"
ThisWorkbook.Sheets(myArr).Select
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, filename:= _
filepath & filename, Quality:=xlQualityStandard, IncludeDocProperties:=True, _
IgnorePrintAreas:=False, OpenAfterPublish:=True
ThisWorkbook.Sheets(1).Select
End Sub
Private Sub UserForm_Initialize()
Copies.Value = 1
FromBox.Value = 1
Dim i As Long
Set mySheets = New Dictionary
For i = 1 To ActiveWorkbook.Sheets.Count
mySheets.Add i, ActiveWorkbook.Sheets(i).Name
SheetBox.Value = SheetBox.Value & i & " - " & ActiveWorkbook.Sheets(i).Name & vbCrLf
Next i
ToBox.Value = i - 1
End Sub
This subroutine takes information from a userform, which has user inputted variables in FromBox and ToBox; these are both longs. The goal is to be able to save, for instance, sheets 2 - 10. The parameters are specified by the user.
the following code, with the bottom section uncommented, works when the user specifies all of the worksheets (IE there are 10 worksheets, and the user specifies range 1-10). But when the user specifies 2-10, it fails.
The problem, I think, is that I'm trying to select 10 elements with a 9 element long array.
As Scott Holtzman pointed out in a comment, you are dimensioning myArr larger than it should be. It therefore has unassigned values in it, which are left as the default zero value, and that causes problems because you don't have a sheet 0 to be selected.
I think the following code should work:
Option Explicit
Dim mySheets As Dictionary
Private Sub SaveAndOpen_Click()
'set up variables
Dim i As Long
Dim j As Long
Dim myArr() As Long
Dim filename As String
'make bounds
Dim from As Long
Dim tonum As Long
'numbers inputted from a userform
from = FromBox.Value
tonum = ToBox.Value
'Check ToBox.Value is valid
If tonum > Sheets.Count Then
MsgBox "Invalid To value"
Exit Sub
End If
'Check FromBox.Value is valid
If from > tonum Then
MsgBox "Invalid From value"
Exit Sub
End If
'Setup myArr
ReDim myArr(from To tonum)
For j = from To tonum
myArr(j) = j
Next
filename = Cells(3, 4) & "." & mySheets.Item(from) & "-" & mySheets.Item(tonum)
'
Dim filepath As String
'For i = LBound(myArr) To UBound(myArr)
' filepath = filepath & myArr(i)
'Next i
filepath = "c:\file\path\here\"
ThisWorkbook.Sheets(myArr).Select
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, filename:= _
filepath & filename, Quality:=xlQualityStandard, IncludeDocProperties:=True, _
IgnorePrintAreas:=False, OpenAfterPublish:=True
ThisWorkbook.Sheets(1).Select
End Sub

Extracting and copying data in an excel file

I'm extracting data froman excel file that is inside the parameter of the dates provided. But this code is not working. Anybody can help me figure this out?
Set src = wb.Sheets("Request Log Extract")
Set dest = ThisWorkbook.Sheets("Resolution Time Performance")
srcRow = src.Cells(src.Rows.Count, "K").End(xlUp).Row
destRow = dest.Cells(dest.Rows.Count, "E").End(xlUp).Row + 1
wb.Activate
For i = 2 To srcRow
If src.Cells("K" & i) >= txtStartDate.Value Or src.Cells("K" & i) <= .txtEndDate.Value Then
src.Cells("K" & i).Copy
dest.Activate
dest.Cells("E" & i).Paste
src.Activate
End If
Next
This returns an error saying :
Invalid procedure call or argument.
NOTE
txtStartDate and txtEndDate are date Types.
If I use OR in the If condition, all data were copied, but if I used And, no data is copied. I don't know whats going on.
VALUES
txtStartDate 05/13/2016
txtEndDate 05/18/2016
k2 05/14/2016
Im not sure with your txtStartDate and txtEndDate variables, but look at my code
I declared your variables, but please specify date types, also i removed dot from txtEndDate and changed cell references and now it works.
Sub extractData()
Dim src
Dim dest
Dim wb As Workbook
Set wb = ThisWorkbook
Dim txtStartDate
Dim txtEndDate
Set src = wb.Sheets("Request Log Extract")
Set dest = ThisWorkbook.Sheets("Resolution Time Performance")
srcRow = src.Cells(src.Rows.Count, "K").End(xlUp).Row
destRow = dest.Cells(dest.Rows.Count, "E").End(xlUp).Row + 1
txtStartDate = 0
txtEndDate = 100
For i = 2 To srcRow
If src.Cells(i, "K").Value > txtStartDate Or src.Cells(i, "K").Value < txtEndDate Then
src.Cells(i, "K").Copy
dest.Activate
dest.Cells(i, "E").PasteSpecial
src.Activate
End If
Next
End Sub
I think it's a date value issue
Moreover I'm guessing your code is within some userform pane and activated at some button click after which it has to compare two textboxes values to some cells content and copy/paste values accordingly
should my guessing be right (finger crossed...) try this:
Option Explicit
Private Sub CommandButton1_Click()
Dim src As Worksheet, dest As Worksheet
Dim srcRow As Long, destRow As Long, i As Long
Dim startDate As Date, endDate As Date, cellDate As Date
With Me
If Not ValidateDate("txtStartDate", .txtStartDate.Value, startDate) Then Exit Sub
If Not ValidateDate("txtEndDate", .txtEndDate.Value, endDate) Then Exit Sub
Set src = ActiveWorkbook.Sheets("Request Log Extract") '<~~ change workbook reference as per your need
Set dest = ThisWorkbook.Sheets("Resolution Time Performance")
srcRow = src.Cells(src.Rows.Count, "K").End(xlUp).Row
destRow = dest.Cells(dest.Rows.Count, "E").End(xlUp).Row + 1
For i = 2 To srcRow
If ValidateDate("src.Range(""K" & i & """)", src.Range("K" & i), cellDate) Then
If cellDate >= startDate And cellDate <= endDate Then src.Range("K" & i).Copy dest.Range("E" & i)
End If
Next
End With
End Sub
Function ValidateDate(textName As String, textValue As String, retDate As Date) As Boolean
ValidateDate = IsDate(textValue)
If ValidateDate Then
retDate = DateValue(textValue)
Else
MsgBox textValue & " is not a valid date" & vbCrLf & "please input a new value for " & textName
End If
End Function
should my guessing be wrong, still the code above can give you some suggestions as to the date value issue
This code is working for me:
Sub Demo()
Dim wb As Workbook
Dim txtStartDate As Date, txtEndDate As Date
Set wb = ActiveWorkbook
Set src = wb.Sheets("Request Log Extract")
Set dest = wb.Sheets("Resolution Time Performance")
srcRow = src.Cells(src.Rows.Count, "K").End(xlUp).Row
destRow = dest.Cells(dest.Rows.Count, "E").End(xlUp).Row + 1
txtStartDate = "05/13/2016"
txtEndDate = "05/18/2016"
For i = 2 To srcRow
If src.Range("K" & i).Value >= txtStartDate And src.Range("K" & i).Value <= txtEndDate Then
src.Range("K" & i).Copy Destination:=dest.Range("E" & i)
End If
Next
End Sub

Loop to go through a list of values

I currently have a macro which goes through a column on my master spreadsheet, then exports all the rows where the value input at the start matches the value in the column. It then saves the new worksheet as the value. Here is the code I currently have:
Option Explicit
Public Const l_HeaderRow As Long = 2 'The header row of the data sheet
Public Const l_DistanceCol As Long = 5 'The column containing the distance values
Public Sub ExportDistance()
Dim ws_Data As Worksheet, wb_Export As Workbook, ws_Export As Worksheet
Dim l_InputRow As Long, l_OutputRow As Long
Dim l_LastCol As Long
Dim l_NumberOfMatches As Long
Dim s_Distance As String, l_Distance As Long
Dim s_ExportPath As String, s_ExportFile As String, s_PathDelimiter As String
Set ws_Data = ActiveSheet
s_Distance = InputBox("Enter Distance to Export to New File", "Enter Distance")
If s_Distance = "" Then Exit Sub
l_Distance = CLng(s_Distance)
l_NumberOfMatches = WorksheetFunction.Match(l_Distance, ws_Data.Columns(5), 0)
If l_NumberOfMatches <= 0 Then Exit Sub
'Application.ScreenUpdating = False
'Application.Calculation = xlCalculationManual
Application.DisplayAlerts = False
On Error Resume Next
Call Application.Workbooks.Add
Set wb_Export = Application.Workbooks(Application.Workbooks.Count)
Set ws_Export = wb_Export.Worksheets(1)
Call wb_Export.Worksheets("Sheet2").Delete
Call wb_Export.Worksheets("Sheet3").Delete
Application.DisplayAlerts = True
ws_Export.Name = GetNextSheetname(ws_Data.Name & "-" & s_Distance, wb_Export)
Call ws_Data.Rows(1).Resize(l_HeaderRow).Copy
Call ws_Export.Rows(1).Resize(l_HeaderRow).Select
Call ws_Export.Paste
l_OutputRow = l_HeaderRow + 1
l_LastCol = ws_Data.UsedRange.Columns.Count
For l_InputRow = l_HeaderRow + 1 To ws_Data.UsedRange.Rows.Count
If ws_Data.Cells(l_InputRow, l_DistanceCol).Value = l_Distance Then
Call ws_Data.Range(ws_Data.Cells(l_InputRow, 1), ws_Data.Cells(l_InputRow, l_LastCol)).Copy
Call ws_Export.Rows(l_OutputRow).Select
Call ws_Export.Paste
l_OutputRow = l_OutputRow + 1
ElseIf ws_Data.Cells(l_InputRow, l_DistanceCol).Value = l_Distance Then
Call ws_Data.Range(ws_Data.Cells(l_InputRow, 1), ws_Data.Cells(l_InputRow, l_LastCol)).Copy
Call ws_Export.Rows(l_OutputRow).Select
Call ws_Export.Paste
l_OutputRow = l_OutputRow + 1
End If
Next l_InputRow
s_ExportPath = ThisWorkbook.Path
s_PathDelimiter = Application.PathSeparator
If Right(s_ExportPath, 1) <> s_PathDelimiter Then s_ExportPath = s_ExportPath & s_PathDelimiter
s_ExportPath = s_ExportPath & "Output" & s_PathDelimiter
If Dir(s_ExportPath) = Empty Then
Call MkDir(s_ExportPath)
End If
Select Case Application.DefaultSaveFormat
Case xlOpenXMLWorkbook
s_ExportFile = s_Distance & ".xlsx"
Case xlOpenXMLWorkbookMacroEnabled
s_ExportFile = s_Distance & ".xlsm"
Case xlExcel12
s_ExportFile = s_Distance & ".xlsb"
Case xlExcel8
s_ExportFile = s_Distance & ".xls"
Case xlCSV
s_ExportFile = s_Distance & ".csv"
Case Else
s_ExportFile = s_Distance
End Select
Call wb_Export.SaveAs(Filename:=s_ExportPath & s_ExportFile, FileFormat:=Application.DefaultSaveFormat)
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
Public Function GetNextSheetname(s_Name As String, Optional wb_Book As Workbook) As String
Dim l_FIndex As Long
Dim s_Target As String
If wb_Book Is Nothing Then Set wb_Book = ActiveWorkbook
s_Name = Left(s_Name, 31)
If IsValidSheet(wb_Book, s_Name) Then
l_FIndex = 1
s_Target = Left(s_Name, 27) & " (" & l_FIndex & ")"
Do While IsValidSheet(wb_Book, s_Target)
l_FIndex = l_FIndex + 1
If l_FIndex < 10 Then
s_Target = Left(s_Name, 27) & " (" & l_FIndex & ")"
ElseIf l_FIndex < 100 Then
s_Target = Left(s_Name, 26) & " (" & l_FIndex & ")"
ElseIf l_FIndex < 1000 Then
s_Target = Left(s_Name, 25) & " (" & l_FIndex & ")"
End If
Loop
GetNextSheetname = s_Target
Else
GetNextSheetname = s_Name
End If
End Function
Public Function IsValidSheet(wbSearchBook As Workbook, v_TestIndex As Variant) As Boolean
Dim v_Index As Variant
On Error GoTo ExitLine
v_Index = wbSearchBook.Worksheets(v_TestIndex).Name
IsValidSheet = True
Exit Function
ExitLine:
IsValidSheet = False
End Function
Please will you help me make this loop through a list of values, rather than my having manually to run the macro each time and input the value myself?
Download this example here.
This is a simple example of how to loop through one range and loop through another range to find the values.
It loops through Column D and then loops through column A, when it finds a match it does something, so basically Column D has taken the place of your inputbox.
run the macro
The code
Sub DblLoop()
Dim aLp As Range 'column A
Dim dLp As Range, dRw As Long 'column D
Dim d As Range, a As Range
Set aLp = Columns("A:A").SpecialCells(xlCellTypeConstants, 23)
dRw = Cells(Rows.Count, "D").End(xlUp).Row
Set dLp = Range("D2:D" & dRw)
'start the loop
'loops through column D and finds value
'in column A, and does something with it
For Each d In dLp.Cells 'loops through column D
For Each a In aLp.Cells 'loops through column A
If d = a Then
'When a match, then do something
'this is where your actual code would go
Range("A" & a.Row & ":B" & a.Row).Copy Cells(Rows.Count, "F").End(xlUp).Offset(1)
End If
Next a 'keeps going through column A
Next d 'next item in column D
End Sub

How to assign value from a named range in one worksheet to a cell in the active worksheet?

I am trying to archive data from formatted worksheet called BOD_Labsheet to one called Data. I have done something similar using UserForms but am encountering problems here.
When I run the macro, I get the error "Method 'Range' of object _Worksheet failed" on line
dataWorksheet.Cells(emptyRow, 2) = bodWorksheet.Range("BOD_Lab_Date").Value
The Data worksheet is active when I do the copying.
Should I simply copy all the values from BOD_Labsheet to an array, activate the Data Worksheet and recopy values?
Here is the complete code:
Sub Submit_BOD()
'
' Submit_BOD Macro
'
Dim dataWorksheet As Worksheet, bodWorksheet As Worksheet, suspendedSolidsWorksheet As Worksheet
Dim dataSheetName As String
Dim bodSheetName As String
Dim suspendedSolidsName As String
dataSheetName = "Data"
bodSheetName = "BOD_Labsheet"
suspendedSolidsName = "Suspended_Solids_Labsheet"
Set dataWorksheet = ActiveWorkbook.Sheets(dataSheetName)
Set bodWorksheet = ActiveWorkbook.Sheets(bodSheetName)
Set suspendedSolidsWorksheet = ActiveWorkbook.Sheets(suspendedSolidsName)
Dim myRanges() As Variant
myRanges = Array("BOD_Collected_By", "BOD_Temp_Out", "BOD_Temp_IN", "BOD_Source", "BOD_Sample_Vol_4", _
"BOD_Dilution_1", "BOD_Blank_IDO_4", "BOD_Blank_FDO_4", "BOD_Sample_Vol_7", "BOD_Dilution_2", _
"BOD_Blank_IDO_7", "BOD_Blank_FDO_7", "BOD_Seed_IDO_13", "BOD_Seed_FDO_13", "BOD_Seed_IDO_14", _
"BOD_Seed_FDO_14", "BOD_Influent_IDO_15", "BOD_Influent_FDO_15", "BOD_Influent_IDO_16", _
"BOD_Influent_FDO_16", "BOD_Effluent_IDO_20", "BOD_Effluent_FDO_20", "BOD_Effluent_IDO_21", "BOD_Effluent_FDO_21", _
"In_BOD_Concentration", "Out_BOD_Concentration")
'Make Data Sheet active
dataWorksheet.Activate
Dim myDate As Date
myDate = DateValue(bodWorksheet.Range("BOD_Lab_Date").Value)
Dim yearAsString As String, monthAsString As String, dayAsString As String
yearAsString = Format(myDate, "yyyy")
monthAsString = Format(myDate, "mm")
dayAsString = Format(myDate, "dd")
Dim reportNumberText As String
reportNumberText = "NP" & yearAsString & monthAsString & dayAsString
Debug.Print "reportNumberText = "; reportNumberText
'Determine emptyRow
Dim emptyRow As Integer
emptyRow = WorksheetFunction.CountA(Range("A:A")) + 1
'Transfer information
'Sample Number
dataWorksheet.Cells(emptyRow, 1).Value = reportNumberText
'Date and Time Collected
dataWorksheet.Cells(emptyRow, 2) = bodWorksheet.Range("BOD_Lab_Date").Value
dataWorksheet.Cells(emptyRow, 3) = Format(bodWorksheet.Range("BOD_Collection_Date").Value, "dd-mmm-yyyy")
dataWorksheet.Cells(emptyRow, 4) = Format(bodWorksheet.Range("BOD_Read_On_Date").Value, "dd-mmm-yyyy")
Dim i As Integer, j As Integer
For i = LBound(myRanges) To UBound(myRanges)
j = i + 4
dataWorksheet.Cells(emptyRow, j) = bodWorksheet.Range(myRanges(i)).Value
Debug.Print "dataWorksheet.Cells(" & emptyRow & "," & j & ") " & dataWorksheet.Cells(emptyRow, j).Value
Next i
ActiveWorkbook.Save
suspendedSolidsWorksheet.Activate
Range("SS_Date").Select
End Sub
Is "BOD_LAB_DATE" more than one cell? Maybe your method usually works also, but I usually would copy a range of cells by reversing your order and using copy, like so:
bodWorksheet.Range("BOD_Lab_Date").Copy dataWorksheet.Cells(emptyRow, 2)

Modeless UserForm won't allow user to change windows

I have a script which will cycle through 500+ files and make minor edits using a find and replace procedure. If the string cannot be found, however, I'd like the code to open a Modeless UserForm which will allow for manual editing. I would have like to execute a different subroutine after opening the UserForm, but because I am using a For Each loop, I cannot exit the routine and then return.
The script works, except when the Save_User is opened, it behaves as if it were Modal. There are two windows which will be open, but I can select neither of them, and the preview of each window is white in the taskbar. Any ideas as to why this might be happening?
Option Explicit
Public WB As Workbook, Template As Workbook
Public FindSelection As Range, ReplaceSelection As Range, FirstRow As Range, SecondRow As Range, StaticRange() As String
Public Target As Range, LastRow As Integer
Public FindRange As String, ReplaceRange As String, FindText As String, ReplaceText As String
Public Count As Integer, Updated As Integer
Public Const FilePath = "C:\GenericPath"
Public Sub AddApplicable()
Dim FSO As Object
Dim Folder As Object
Dim File As Object
Set FSO = CreateObject("Scripting.FileSystemObject")
Set Folder = FSO.GetFolder(FilePath)
Count = 0
Updated = 0
For Each File In Folder.Files
If File.Type Like "Microsoft Excel*" Then
Application.ScreenUpdating = False
Set WB = Workbooks.Open(File.Path)
Set FindSelection = WB.Worksheets("Repair Instruction").Range("K17:W40")
Set ReplaceSelection = WB.Worksheets("Repair Instruction").Range("X17:AJ40")
LastRow = WB.Worksheets("Repair Instruction").Range("K40:W40").End(xlUp).Row
Set Target = FindSelection.Find(FindText, LookAt:=xlWhole)
If Target Is Nothing Then
If LastRow = 39 Then
Save_User.Show vbModeless
While Save_User.Visible
DoEvents
Wend
GoTo NextAdd
Else
If LastRow Mod 2 = 0 Then
WB.Worksheets("Repair Instruction").Range("K" & LastRow + 1, "W" & LastRow + 1) = FindText
WB.Worksheets("Repair Instruction").Range("X" & LastRow + 1, "AJ" & LastRow + 1) = _
Add_User.txtFirst.Value
WB.Worksheets("Repair Instruction").Range("X" & LastRow + 2, "AJ" & LastRow + 2) = _
Add_User.txtSecond.Value
Else
WB.Worksheets("Repair Instruction").Range("K" & LastRow + 2, "W" & LastRow + 2) = FindText
WB.Worksheets("Repair Instruction").Range("X" & LastRow + 2, "AJ" & LastRow + 2) = _
Add_User.txtFirst.Value
WB.Worksheets("Repair Instruction").Range("X" & LastRow + 3, "AJ" & LastRow + 3) = _
Add_User.txtSecond.Value
End If
WB.Close True
Updated = Updated + 1
End If
Else
Set FirstRow = WB.Worksheets("Repair Instruction").Range("X" & Target.Row, "AJ" & Target.Row)
Set SecondRow = WB.Worksheets("Repair Instruction").Range("X" & Target.Row + 1, "AJ" & Target.Row + 1)
FirstRow.Value = Add_User.txtFirst.Value
SecondRow.Value = Add_User.txtSecond.Value
WB.Close True
End If
End If
NextAdd:
Count = Count + 1
Next File
Unload Applicable_User
Unload Add_User
Unload Ref_User
Unload Replace_User
MsgBox (Updated & " of " & Count & " files updated.")
End Sub
EDIT
David, thank you! I wouldn't have even considered that as the reason, but I'm grateful that it is relatively simple! I am not aware of any other functions that would hide the cycling Excel files, so I edited the If statement with the Save_User to be:
If LastRow = 39 Then
Application.ScreenUpdating = True
WB.Activate
Save_User.Show vbModeless
While Save_User.Visible
DoEvents
Wend
Application.ScreenUpdating = False
GoTo NextAdd
...
End If
The cycling Excel windows now appear for editing, but I can't actually make any changes (my cursor is now a crosshair, and I can select cells but not change their values). None of the Workbooks should be protected, so is there something that I'm missing? Or perhaps a better way to hide the activity in the background (because I don't want 500+ files to appear and disappear)?
Here's your culprit:
Application.ScreenUpdating = False
This prevents user input.