VBA - Sheets.count - subscript out of range - vba

Below is a macro which I wrote about three years ago, when I was a much less proficient VBA coder than I am today. There are a number of obvious things which I would simplify/do differently today. However, it is still in use and generally works. The manager of the relevant admin process copies and pastes this code into different workbooks every time we set up a new customer, and changes around a few of the variables. This has worked fine until today, when it has suddenly started generating a "subscript out of range" error when used in a new worksheet.
The error generates on the line workdaybook.Sheets("Sheet1").Copy salesBook.Worksheets(Sheets.Count). I have checked and "salesBook" has been defined. However, hovering the cursor over "salesBook.worksheets(Sheets.Count) brings up a "subscript out of range" message.
I know that overall, this code isn't the best-written in the world, but I am puzzled by it suddenly failing on this line, having worked previously for about three years when pasted into multiple different workbooks.
Option Explicit
Sub salescalc()
'Application.DisplayAlerts = False
'On Error GoTo Errorcatch
Application.ScreenUpdating = True
Dim salesBook As Workbook
Dim CurrentWeekSheet As Worksheet
Set salesBook = ThisWorkbook
Set CurrentWeekSheet = ThisWorkbook.ActiveSheet
Dim workday As Date
Dim nextworkday As Date
Dim workdaybook As Workbook
workday = InputBox("Insert date in format dd/mm/yy", "userdate")
nextworkday = workday + 1
'find bottom row of "table"
Dim bottomrow As Range
For x = 1 To 6500
If CurrentWeekSheet.Cells(x, 1).Interior.ColorIndex = 19 Then
coloured_row = Range("A" & x).Row
End If
Next x
Set bottomrow = Range("A" & coloured_row)
'finds beginning and end of day's range
Dim workdayrange As Range
Dim nRow As Long
Dim workday_date As Variant
Dim nStart As Long, nEnd As Long
' Work out where the range should start - works
For nRow = 1 To 65536 'change this to xldown
If CurrentWeekSheet.Range("A" & nRow).Value = workday Then
'nStart = nRow + 3
nStart = nRow + 1
Exit For
End If
Next nRow
' Work out where the range should end - works
For nRow = nStart To 65536
If CurrentWeekSheet.Range("A" & nRow).Value = nextworkday Or Range("A" & nRow).Row = bottomrow.Row Then
nEnd = nRow
Exit For
End If
Next nRow
'distinction between bottom row and next date - offset less for bottomrow
If nEnd = bottomrow.Row Then
nEnd = nEnd
Else
If nEnd <> bottomrow.Row Then
nEnd = nEnd - 2
End If
End If
Set workdayrange = CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd)
workday_date = Format(workday, "dd-mm-yy")
'identify which month is referred to based on date input by user (workday_date)- this will need to be updated in 2016
If Year(workday_date) <> 2017 Then
MsgBox "Date must be in 2017. If you require info for another year, please contact xxxxx."
Exit Sub
Else
Workbooks.Open ("U:\\(Folder)\\(subfolder)\\(Subfolder)\\2017\\" & workday_date & ".xlsx")
Worksheets("Sheet1").Activate
End If
Set workdaybook = ActiveWorkbook
Dim workdaysheet As Worksheet
Set workdaysheet = ActiveSheet
workdaybook.Activate
workdaybook.Sheets("Sheet1").Copy salesBook.Worksheets(Sheets.Count)
ActiveSheet.Name = "salesdata"
Dim sheetforcopy As Worksheet
Set sheetforcopy = Sheets.Add
sheetforcopy.Name = "Sheetforcopy"
CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd).EntireRow.Copy
sheetforcopy.Range("A185").PasteSpecial xlPasteValues
sheetforcopy.Range("F" & nStart & ":F" & nEnd).Formula = "=if(D185<>0,SUMIFS(salesdata!E:E,salesdata!B:B,""*""&D185&""*"",salesdata!B:B,""*Total*""),"""")"
sheetforcopy.Range("L" & nStart & ":L" & nEnd).Formula = "=IF(IF(ISNUMBER(B185)=TRUE,COUNTIF(salesdata!B:B,""*""&B185&""*"")-1,"""")=-1,0,IF(ISNUMBER(B185)=TRUE,COUNTIF(salesdata!B:B,""*""&B185&""*"")-1,""""))"
sheetforcopy.Range("F" & nStart & ":F" & nEnd).Copy
CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd).PasteSpecial xlPasteValues
CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd).Copy
CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd).PasteSpecial xlPasteValues
sheetforcopy.Range("L" & nStart & ":L" & nEnd).Copy
CurrentWeekSheet.Range("L" & nStart & ":L" & nEnd).PasteSpecial xlPasteValues
CurrentWeekSheet.Range("L" & nStart & ":L" & nEnd).Copy
CurrentWeekSheet.Range("L" & nStart & ":L" & nEnd).PasteSpecial xlPasteValues
Application.DisplayAlerts = False
Worksheets("salesdata").Delete
Worksheets("sheetforcopy").Delete
Application.DisplayAlerts = True
workdaybook.Close
CurrentWeekSheet.Activate
CurrentWeekSheet.Range("F" & nStart).Select
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Exit Sub
'Errorcatch: MsgBox "Error - (confidential message deleted)."
End Sub

Related

Excel VBA: Merge worksheets by search/mapping headers?

Looking to up my manual mapping solution of merging worksheets, to a search-map header solution. The basics are this
Dest_Worksheet: This has the only headers that are needed post merge (up to 50 columns)
Source_Worksheet1: This has a list of items with some headers that match Dest_Worksheet (up to 100 columns - different than Source_Worksheet2)
Source_Worksheet2: This has a list of items with some headers that match Dest_Worksheet (up to 100 columns - different than Source_Worksheet1)
Total row count unknown at the time of run. Currently built out a manual mapping (see below).
ASKING: Move beyond manual mapping of each worksheet to a solution which reviews the Dest_Worksheet and references those headers, move through remaining or identified list of Source worksheets and copy all rows with only columns that match Dest_Worksheet.
See sample worksheet for working manual mapping code below
'******Manual Mapping of Source_Data1*******
Sub Source_Data1()
Dim sht As Worksheet
Dim colname As String
Dim Lastrow As Integer, rowcount As Integer
colname = 1
For Each sht In ActiveWorkbook.Worksheets
If sht.Name = "Source_Worksheet1" And sht.Range("A3").Value <> "" Then
Sheets("Source_Worksheet1").Select
Lastrow = Range("A9000").End(xlUp).Row
Sheets("Dest_Worksheet").Select
rowcount = Range("A9000").End(xlUp).Row + 1
sht.Select
Sheets("Dest_Worksheet").Range("A" & rowcount & ":A" & rowcount + Lastrow - 3).Value = sht.Range("A3:A" & Lastrow).Value
Sheets("Dest_Worksheet").Range("B" & rowcount & ":B" & rowcount + Lastrow - 3).Value = sht.Range("B3:B" & Lastrow).Value
Sheets("Dest_Worksheet").Range("C" & rowcount & ":C" & rowcount + Lastrow - 3).Value = sht.Range("C3:C" & Lastrow).Value
Sheets("Dest_Worksheet").Range("D" & rowcount & ":D" & rowcount + Lastrow - 3).Value = sht.Range("D3:D" & Lastrow).Value
End If
Next sht
Worksheets("Dest_Worksheet").Select
End Sub
'******Manual Mapping of Source_Data2*******
Sub Source_Data2()
Dim sht As Worksheet
Dim colname As String
Dim Lastrow As Integer, rowcount As Integer
colname = 1
For Each sht In ActiveWorkbook.Worksheets
If sht.Name = "Source_Worksheet2" And sht.Range("A3").Value <> "" Then
Sheets("Source_Worksheet2").Select
Lastrow = Range("A9000").End(xlUp).Row
Sheets("Dest_Worksheet").Select
rowcount = Range("A9000").End(xlUp).Row + 1
sht.Select
Sheets("Dest_Worksheet").Range("A" & rowcount & ":A" & rowcount + Lastrow - 3).Value = sht.Range("A3:A" & Lastrow).Value
Sheets("Dest_Worksheet").Range("E" & rowcount & ":E" & rowcount + Lastrow - 3).Value = sht.Range("B3:B" & Lastrow).Value
Sheets("Dest_Worksheet").Range("F" & rowcount & ":F" & rowcount + Lastrow - 3).Value = sht.Range("C3:C" & Lastrow).Value
Sheets("Dest_Worksheet").Range("C" & rowcount & ":C" & rowcount + Lastrow - 3).Value = sht.Range("E3:E" & Lastrow).Value
End If
Next sht
Worksheets("Dest_Worksheet").Select
End Sub
After lots of trial and error I got Find() working to return the column letter I'm looking for. Here's the code I ended up using and the associated function call:
Sub LookupText()
Dim DestLetter As String
DestLetter = TextLookup("Search Text")
MsgBox DestLetter
End Sub
'***********
Function TextLookup(TheText As String) As String
Set Cell = Worksheets("Destination_Worksheet").Cells.range("A1:DA1").Find(TheText, , xlValues, xlPart, , , False)
If Not Cell Is Nothing Then
ColLetter = Split(Cell.Address, "$")(1)
TextLookup = ColLetter
End If
End Function

deleting rows with blank cells and criteria VBA

I have columns from A - S, where I need to delete the headers and blank cells, my criteria for lookup in deleting headers are "Transaction" & "Source" but it seems it's skipping rows. I have a total of 79,000 rows but code only goes till 39,000. I've tried everything I can find over. still nothing happens.
I'm also starting the formatting and deleting on row 209 up to lastrow.
Option Explicit
Sub Project_M()
Dim lastrow As Long
Dim cc As Long
Dim dd As Long
lastrow = WorksheetFunction.CountA(Columns(1))
Application.ScreenUpdating = False
Call ClearFormats
lastrow = WorksheetFunction.CountA(Columns(1))
Columns(1).Insert shift:=xlToRight
Range("A209:A" & lastrow).Formula = "=ROW()" 'inserting dummy rows
Range("A209:A" & lastrow).Value = Range("A210:A" & lastrow).Value
Range("U209:U" & lastrow).Formula = "=IF(AND(ISERROR(SEARCH(""Transaction"",B209)),ISERROR(SEARCH(""Source"", B209))),1,0)"
Range("U209:U" & lastrow).Value = Range("U209:U" & lastrow).Value
''''' delete headers : only working till row 39,0000
Range("A209:U" & lastrow).Sort Key1:=Range("U209"), Order1:=xlAscending
cc = WorksheetFunction.CountIf(Columns(21), "0")
If cc <> 0 Then
Range("A209:U" & cc).Select
Range("A209:U" & cc).EntireRow.Delete
lastrow = lastrow - cc
End If
Range("A209:U" & lastrow).Sort Key1:=Range("A209"), Order1:=xlAscending
Range("U:U").ClearContents
Range("A:A").Delete
ActiveSheet.UsedRange.Columns.AutoFit
End Sub
Sub deleteBlank() 'not working
Dim lastrow As Integer
lastrow = Range("A" & rows.Count).End(xlUp).Row
Range("B2:B" & lastrow).SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End Sub
Sub ClearFormats() ' working
Dim rng As Range
Dim lastrow As Long
Dim ws As Worksheet
lastrow = Range("A" & rows.Count).End(xlUp).Row
Application.ScreenUpdating = False
On Error Resume Next
Set rng = Range("A209:S" & lastrow).SpecialCells(xlCellTypeBlanks)
On Error GoTo 0
If Not rng Is Nothing Then
rng.ClearFormats
End If
On Error Resume Next 'not working in deleting blank cells
ws.Columns("A209:S" & lastrow).SpecialCells(xlCellTypeBlanks).EntireRow.Delete
On Error GoTo 0
End Sub
Sub DeleteExtra() ' not working
Dim Last As Long
Dim i As Long
Last = Cells(rows.Count, "A").End(xlUp).Row
For i = Last To 1 Step 1
If (Cells(i, "A209").Value) = "Transaction" And (Cells(i, "A209").Value) = "Source" And (Cells(i, "A209").Value) = "" And (Cells(i, "A209").Value) = " " Then
Cells(i, "A").EntireRow.Delete
End If
Next i
End Sub
Sub deleteBlankcells() '''not working
Dim lastrow As Long
Dim cc As Long
lastrow = WorksheetFunction.CountA(Columns(1))
Range("A209:A" & lastrow).Formula = "=ROW()" 'inserting dummy rows
Range("A209:A" & lastrow).Value = Range("A210:A" & lastrow).Value
Range("U209:U" & lastrow).Formula = "=IF(AND(ISBLANK(A209),ISBLANK(A209)),0,1)"
Range("U209:U" & lastrow).Value = Range("U209:U" & lastrow).Value
Range("A209:U" & lastrow).Sort Key1:=Range("U209"), Order1:=xlAscending
cc = WorksheetFunction.CountIf(Columns(21), "0")
If cc <> 0 Then
Range("A209:U" & cc).Select
Range("A209:U" & cc).EntireRow.Delete
lastrow = lastrow - cc
End If
Range("A209:U" & lastrow).Sort Key1:=Range("A209"), Order1:=xlAscending
Range("U:U").ClearContents
Range("A:A").Delete
End Sub
I've tried different attempts but not working. codes are commented.
Thanks!
With the help and ideas of users, I've come to this simple code and got it working.
Credits to all of them! Cheers!
Option Explicit
Sub Project_M()
Dim Last As Long
Dim i As Long
Application.ScreenUpdating = False
Last = cells(rows.Count, "A").End(xlUp).Row
Range("A209:S" & Last).UnMerge
Range("A209:S" & Last).WrapText = False
For i = Last To 209 Step -1
If (cells(i, "A").Value) = "Source" Or (cells(i, "A").Value) = 0 Or (cells(i, "A").Value) = "End of Report" Or (cells(i, "A").Value) = "Transaction" Then
cells(i, "A").EntireRow.Delete
End If
Next i
ActiveSheet.UsedRange.Columns.AutoFit
Application.ScreenUpdating = True
End Sub
Starting from the last row of the column for i = Last up to the row I want to start my formatting and deleting To 209 and Step -1 to move up.

VBA pasting row as image

Just have a quick question regarding paste. I have a script that exports individual rows into a newly created workbook. However, the problem is that the pasted values are in the form of image. Additionally, the comments are skipped. I used the same code for pasting into other sheets of the same workbook, and there is no issue.
I can't seem to find the reason why. Any help would be greatly appreciated.
Thanks
Private Sub DC_1Month_Button_Click()
'Searches for crews working on MFDC (7343) and exports a new spreadsheet looking 3 weeks ahead for each person
If MsgBox("Export DC individual schedules?") = vbNo Then
Exit Sub
End If
On Error GoTo CleanFail
Dim nowCol As Integer, lastCol As Integer, endCol As Integer, crewRow As Integer
Dim masterSheet As Worksheet, newExcel As Object, newBook As Workbook, newSheet As Worksheet
Dim startRow As Integer, endRow As Integer
Dim currentName As String, currentProject As String
startRow = 3
endRow = UsedRange.Row - 1 + UsedRange.Rows.count
lastcoln = UsedRange.Column - 1 + UsedRange.Columns.count
Set masterSheet = ThisWorkbook.Worksheets("Master Schedule")
'Find columns for today and date 3 weeks after
nowCol = Range(Cells(2, 1), Cells(2, lastcoln)).Find(what:=Month(Date) & "/" & Day(Date) & "/" & Year(Date)).Column
endCol = Range(Cells(2, 1), Cells(2, lastcoln)).Find(what:=Month(DateAdd("d", 30, Date)) & "/" & Day(DateAdd("d", 30, Date)) & "/" & Year(DateAdd("d", 30, Date))).Column
'Disable screen flashing while doing copying and exports
Application.ScreenUpdating = False
'Loop through crew members
For i = 3 To endRow
'Store current row's values
currentName = Replace(ActiveSheet.Cells(i, 2).Value, "SA: ", "")
currentProject = ActiveSheet.Cells(i, 3).Value
'Search the value from the Project column for the MFDC project number
If InStr(1, currentProject, "7343") > 0 Then
'Load schedule template
Set newExcel = CreateObject("Excel.Application")
newExcel.DisplayAlerts = False
newExcel.Workbooks.Open "\\VALGEOFS01\SurveyProjectManagers\304Schedule\Templates\DC_3Week_Template.xlsx"
Set newBook = newExcel.Workbooks(1)
Set newSheet = newBook.Worksheets(1)
'Copy and paste header rows
masterSheet.Range(masterSheet.Cells(1, nowCol), masterSheet.Cells(2, endCol)).Copy 'Destination:=newSheet.Range("A1")
Application.Wait (Now + TimeValue("0:00:01"))
newSheet.Range(newSheet.Cells(1, 6), newSheet.Cells(1, endCol - 1)).PasteSpecial xlPasteValuesAndNumberFormats
Application.CutCopyMode = False
'Copy and paste crew member's location
masterSheet.Range(masterSheet.Cells(i, 2), masterSheet.Cells(i, 6)).Copy 'Destination:=newSheet.Range("A3")
Application.Wait (Now + TimeValue("0:00:01"))
newSheet.Range("A3").PasteSpecial Paste:=xlPasteValuesAndNumberFormats
Application.CutCopyMode = False
'Copy schedule data for crew member
masterSheet.Range(masterSheet.Cells(i, nowCol), masterSheet.Cells(i, endCol)).Copy
Application.Wait (Now + TimeValue("0:00:01"))
newSheet.Cells(3, 6).PasteSpecial Paste:=xlPasteAll
Application.CutCopyMode = False
'Save individual's schedule
With newBook
.Title = currentName & " MFDC Schedule"
.SaveAs Filename:="\\VALGEOFS01\SurveyProjectManagers\304Schedule\MFDC Individual Schedules\" & currentName & " MFDC Schedule " & Format(Date, "yymmdd") & ".xlsx", AccessMode:=xlExclusive, ConflictResolution:=Excel.XlSaveConflictResolution.xlLocalSessionChanges
.Close (True)
End With
End If
Next i
CleanExit:
MsgBox "Export complete"
'Restore normal screen updating
Application.ScreenUpdating = True
Exit Sub
CleanFail:
If Err.Number <> 0 Then
Msg = "Error # " & Str(Err.Number) & " was generated by " & Err.Source & Chr(13) & Err.Description
MsgBox Msg, , "Error", Err.HelpFile, Err.HelpContext
End If
Resume CleanExit
Resume
End Sub

How to create excel VBA change log

I am trying to write a change log for excel VBA. I want it to iterate through so that the each additional response is populated in the workbook as the additional rows. Please let me know if you have any insight into what is wrong with my code
Dim streply As String
Dim Today As Date
Dim myrange As Range
Dim inglastrow As Long
CurrentDate = Date
With Sheets("Sheet1")
lastrow = .Range("A" & .Rows.Count).End(xlUp).Row
nextrow = lastrow + 1
MsgBox lastrow
MsgBox nextrow
End With
MsgBox lastrow
streply = InputBox(Prompt:="Please type description of changes", Title:="Change Log", Default:="Brief Desc.")
If streply <> " " Then
Range("A" & nextrow).Value = Application.UserName
Range("B" & nextrow).Value = streply
Range("C" & nextrow).Value = ActiveWorkbook.Name
Range("D" & nextrow).Value = Date
End If
Set lastrow = Nothing
Set nextrow = Nothing
End Sub
EDIT: silly mistake on my part, fixed now
Instead of:
lastrow = .Range("A" & .Rows.Count).End(xlUp).Row
Try:
lastrow = .UsedRange.Rows.Count

Looping through range using VBA in Excel

I have a block of code that takes way too long to process for some files. Smaller files (fewer lines of data) work fine, but once I get to about 150-300, it starts to get slow, (sometimes I think the whole process actually just hangs) and I have to run this sometimes on files with up to 6,000.
I want to plug in a VLookup() function in the .FormulaR1C1 for a number of cells. I know that I can set the whole range at once using .Range("J2:J" & MaxRow). However, I am looping through a block of cells to check the value of those cells. IF they are empty, THEN I want to apply the formula. If those cells already have values, then I don't want to change them, so I don't think the whole range option will work for me (at least I was unable to get it right).
Private Sub PullMIAFinalizedData(NewMIARep As Worksheet, MaxRow As Long, wkbFinalized As Workbook)
Dim wksFinalized As Worksheet
Dim lCount As Long
Dim sVLookupJBlock As String
Dim sVLookupKBlock As String
Application.Calculation = xlCalculationManual
sVLookupJBlock = "=IF(ISERROR(" & _
"VLOOKUP(RC1,'[" & wkbFinalized.Name & "]" & wksFinalized.Name & "'!C1:C13,13,FALSE))," & _
Chr(34) & Chr(34) & _
",VLOOKUP(RC1,'[" & wkbFinalized.Name & "]" & wksFinalized.Name & "'!C1:C13,13,FALSE))"
sVLookupKBlock = "=IF(ISERROR(" & _
"VLOOKUP(RC1,'[" & wkbFinalized.Name & "]" & wksFinalized.Name & "'!C1:C3,3,FALSE))," & _
Chr(34) & Chr(34) & _
",VLOOKUP(RC1,'[" & wkbFinalized.Name & "]" & wksFinalized.Name & "'!C1:C3,3,FALSE))"
For Each wksFinalized In wkbFinalized.Sheets
ShowAllRecords wksFinalized 'Custom Function to unhide/unfilter all data
With NewMIARep
For lCount = 2 To MaxRow
If .Range("J" & lCount).value = "" And .Range("K" & lCount).value = "" Then
.Range("J" & lCount).FormulaR1C1 = sVLookupJBlock
.Range("K" & lCount).FormulaR1C1 = sVLookupKBlock
Application.Calculate
With .Range("J" & lCount & ":K" & lCount)
.value = .value
End With
End If
Next lCount
.Range("J2:J" & MaxRow).NumberFormat = "mm/dd/yyyy"
End With
Next wksFinalized
Application.Calculation = xlCalculationAutomatic
End Sub
Am I just stuck with this?
Thanks very much to assylias and Siddharth Rout for helping out with this; both provided very useful information, which led to this result:
Private Sub PullMIAFinalizedData(NewMIARep As Worksheet, MaxRow As Long, wkbFinalized As Workbook)
Dim wksFinalized As Worksheet
Dim lCount As Long
Dim lFinMaxRow As Long
Dim DataRange As Variant 'per assylias, using a variant array to run through cells
Dim FoundRange As Range
Application.Calculation = xlCalculationManual
With NewMIARep
DataRange = .Range("J2:K" & MaxRow)
For Each wksFinalized In wkbFinalized.Sheets
ShowAllRecords wksFinalized
lFinMaxRow = GetMaxRow(wksFinalized)
If lFinMaxRow > 1 Then
For lCount = 1 To MaxRow - 1
If Len(DataRange(lCount, 1)) = 0 And Len(DataRange(lCount, 2)) = 0 Then
'per Siddharth Rout, using Find instead of VLookup
Set FoundRange = wksFinalized.Range("A2:A" & lFinMaxRow).Find(What:=.Range("A" & lCount).value, LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
If Not FoundRange Is Nothing Then
DataRange(lCount, 1) = FoundRange.Offset(ColumnOffset:=12).value
DataRange(lCount, 2) = FoundRange.Offset(ColumnOffset:=2).value
Set FoundRange = Nothing
End If
End If
Next lCount
End If
Next wksFinalized
.Range("J2:K" & MaxRow).value = DataRange
.Range("J2:J" & MaxRow).NumberFormat = "mm/dd/yyyy"
End With
Application.Calculation = xlCalculationAutomatic
End Sub
You don't want to iterate on cells from VBA: it is EXTREMELY slow. Instead, you put the data you need into an array, work on the array and put the data back to the sheet. In your case, something like the code below (not tested):
Dim data as Variant
Dim result as Variant
Dim i as Long
data = ActiveSheet.UsedRange
ReDim result(1 To UBound(data,1), 1 To UBound(data,2)) As Variant
For i = LBound(data,1) to UBound(data,1)
'do something here, for example
If data(i,1) = "" Then
result(i,1) = "=VLOOKUP($A1,$A:$G," & i & ",FALSE)"
Else
result(i,1) = data(i,1)
End If
Next i
ActiveSheet.Cells(1,1).Resize(Ubound(result, 1), UBound(result,2)) = result