Create a Do While loop to scan a column until no value is found in VBA - excel

I worked on the top portion of this last week and that doesn't need anything aside from some formatting on my end. The bottom portion with the block style comments are where I am stuck. (below wks.Activate is new)
I am trying to set a do while loop that sequentially reads through a given column until no value is present, then copy the row above and paste the formatting into the blank row.
There are a couple other problems, but I'm just concerned with base functionality at the moment.
The code I have put together so far is as follows:
Public Sub AddNewPage()
Sheets(Sheets.Count).Select 'references last sheet in workbook
Dim wks As Worksheet 'establish static variable wks to reference worksheets
Dim nullVal As Boolean 'set a boolean variable for value check
Dim i As Integer 'set variable as integer for coming loop
Set wks = ActiveSheet 'set wks to be the given, activated sheet (dynamic variable)
ActiveSheet.Copy After:=Worksheets(Sheets.Count) 'sets the last sheet in the workbook as the active sheet and copies it
Range("H9").Value = Range("H9").Value + 1 'sets value of cell H9 in new worksheets to sequentially increase by 1
If wks.Range("H9").Value <> "" Then 'If cell "H9" in activated worksheet has a value then...
On Error Resume Next 'Proceed even if I beak stuff
wks = Sheets(Sheets.Count).Select 'sets wks to reference last sheet in given workbook
ActiveSheet.Name = wks.Range("H9").Value + 1 'sets page title to sequentially increase by 1 with each iteration
End If
wks.Activate
Application.Worksheets("Log").Activate 'redirect to primary sheet used for tracking workbook data
Do While nullVal = False 'establishing a do while loop to scan cells until no value is found
For i = 1 To 1000 '*************************
If Cells(i, 1).Value <> "" Then '*This is my problem
nullVal = False '*area, I think.
i = i + 1 '*
Else: nullVal = True '*The goal is to create
End If '*a do while loop that
Next i '*scans cells in the given
'*column until no value
If nullVal = True Then Exit Do '*is found and copy the
If nullVal = True Then Exit Sub '*above row's formatting
'*into the blank row.
Loop '*(see below)
'*************************
If nullVal = True Then
wks.Cells(i, 1).End(xlUp).Offset(1, 0).Select '**********************************
Rows(Selection.Row - 1).Copy '*Another minor bug in here as it
Rows(Selection.Row).Insert Shift:=xlDown '*won't necessarily select the last
Rows(Selection.Row).ClearContents '*row if the user is in the sheet
Application.CutCopyMode = False
End If
End Sub
It's somewhat functional at this point, with bugs.
Any other sets of eyes and any depth of knowledge would be appreciated as I just started learning this... thing (VBA) and I've been working on and off with this for days now.
I am curious as to whether an array or table might serve me well here.

I'm not sure by saying copy the format to the empty [row] means the entire row or just the empty cell?
For you loop struct
Dim vItem As Variant
For Each vItem In Selection
If vItem.Value = "" Then
vItem.Activate
ActiveCell.Offset(-1, 0).Copy
ActiveCell.PasteSpecial xlPasteFormats
Exit Sub
End If
Next vItem
This will format the empty cell by the format of the cell immediately above it.
Is this what you want?

Public Sub AddNewPage()
Sheets(Sheets.Count).Select 'references last sheet in workbook
Dim wks As Worksheet 'establish static variable wks to reference worksheets
Dim nullVal As Boolean 'set a boolean variable for value check
Dim i As Integer 'set variable as integer for coming loop
Set wks = ActiveSheet 'set wks to be the given, activated sheet (dynamic variable)
Dim iRange As Range
Dim iCells As Range
ActiveSheet.Copy After:=Worksheets(Sheets.Count) 'sets the last sheet in the workbook as the active sheet and copies it
Range("H9").Value = Range("H9").Value + 1 'sets value of cell H9 in new worksheets to sequentially increase by 1
If wks.Range("H9").Value <> "" Then 'If cell "H9" in activated worksheet has a value then...
On Error Resume Next 'Proceed even if I beak stuff
wks = Sheets(Sheets.Count).Select 'sets wks to reference last sheet in given workbook
ActiveSheet.Name = wks.Range("H9").Value + 1 'sets page title to sequentially increase by 1 with each iteration
End If
wks.Activate
Application.Worksheets("Log").Activate 'redirect to primary sheet used for tracking workbook data
Do While nullVal = False 'establishing a do while loop to scan cells until no value is found
For i = 1 To Sheets(Sheet.Count).Value 'Makes the book functional, but because I have an index page and a master template I have compensate with "- 2" in the subsequent code
If Cells(i, 1).Value > "" Then 'If cells in defined column have positive value, adhere to the following
nullVal = False
Else: nullVal = True
End If
Next i 'Psuedo ++ style operator
If nullVal = True Then
Cells(i - 1, 1) = i - 2 'Floating variable which sets numeric value of a given cell to coincide with page count
End If
Loop
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'The following makes use of the floating variable "i - 2" to
'define the page number for the given formulas
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Cells(i - 1, 1).Select 'Creates a hyperlink to sheets as they are being created
ActiveCell.Formula = "=('" & i - 2 & "'!H9)"
Sheets("Log").Cells(i - 1, 1).Hyperlinks.Add Selection, Address:="", SubAddress:="'" & Sheets(Sheets.Count).Name & "'!H9", TextToDisplay:=""
Cells(1, 1).Select 'Solves the issue of replacing descriptor in cell (1, 1)
Sheets("Log").Cells(1, 1).Hyperlinks.Add Selection, Address:="", SubAddress:="", TextToDisplay:="EWA #"
Cells(i - 1, 2).Select
ActiveCell.Formula = "=('" & i - 2 & "'!A12)"
Cells(i - 1, 3).Select
ActiveCell.Formula = "=('" & i - 2 & "'!H24)"
Cells(i - 1, 4).Select
ActiveCell.Formula = "=('" & i - 2 & "'!H25)"
Cells(i - 1, 5).Select
ActiveCell.Formula = "=('" & i - 2 & "'!D29)"
Cells(i - 1, 6).Select
ActiveCell.Formula = "=('" & i - 2 & "'!E29)"
Cells(i - 1, 7).Select
ActiveCell.Formula = "=('" & i - 2 & "'!H42)"
Cells(i - 1, 1).EntireRow.RowHeight = 50 'Row width formatting for new data
Cells(i - 1, 1).Select 'Moving back to the top of the page
End Sub
That is what I was trying to get to, took a bit and I'm still not completely familiarized with VBA. If anyone has any insightts on how to clean it up a bit I'm all ears.
I would like to find a way to create a page from the base master template, called "master," have the created page populate with the name "1" and populate the index page with a 1 as well while no values are present.

Related

VBA - Combine Tables to Add Unique Rows

I have a workbook that is being updated regularly by third parties. Let's call each update WB1, WB2... The data in WB is formatted as a table in columns A:F, and there are approx. 2000 rows. There is one sheet of data. In my copy of WB, I called it "Master." In WB1, WB2..., it is "Indexes." Column A has a unique identifier for each row, and the rest of the data is text.
I'm adding notes next to each row, in columns G:H. I need to be able to merge the unique entries from WB 1 into my copy of WB, while preserving my notes in G:H, and the conditional formatting I added to WB. I want to use VBA, and I do not have Microsoft Access.
I found a partial solution here: Find Duplicate Values In Excel and Export Rows to another sheet using VBA
I made the following changes to the solution linked above:
Option Explicit
Sub MergeTables()
Dim wstSource As Worksheet, _
wstOutput As Worksheet
Dim rngMyData As Range, _
helperRng As Range, _
unionRng As Range
Dim i As Long, iOld As Long
Set wstSource = Worksheets("Indexes")
Set wstOutput = Worksheets("Master")
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
With wstSource
Set rngMyData = .Range("A1:J" & .Range("A" & .Rows.Count).End(xlUp).Row)
End With
With rngMyData
Set helperRng = .Offset(, rngMyData.Columns.Count - 1).Resize(, 1)
Set unionRng = .Cells(3000, 3000) 'set a "helper" cell to be used with Union method, to prevent it from failing the first time
End With
With helperRng
.FormulaR1C1 = "=row()" 'mark rows with ad ascending number (its own row number)
.Value = .Value
End With
With rngMyData.Resize(, rngMyData.Columns.Count + 1) 'enclose "helper" column
i = .Rows(1).Row 'start loop from data first row
Do While i < .Rows(.Rows.Count).Row
iOld = i 'set current row as starting row
Do While .Cells(iOld + 1, 1) = .Cells(iOld, 1) 'loop till first cell with different value
iOld = iOld + 1
Loop
If iOld - i = 0 Then Set unionRng = Union(unionRng, .Cells(i, 1).Resize(iOld - i + 1)) 'if more than one cell found with "current" value, then add them to "UnionRng" range
i = iOld + 1
Loop
Intersect(unionRng, rngMyData).Range("A:F").Copy Destination:=wstOutput.Cells(1, 1) 'get rid of the "helper" cell via Intersect method
wstOutput.Columns(helperRng.Column).Clear 'delete "Helper" column pasted in wstOutput sheet
.Sort key1:=.Columns(6), Order1:=xlAscending, Orientation:=xlTopToBottom, Header:=xlYes ' sort data in wstSource back
End With
helperRng.Clear 'delete "helper" column, not needed anymore
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
This macro successfully adds in the new rows, but my notes in G:H do not line up with the original data anymore.
Can you please suggest alternative approaches, or revisions to this macro?
Thanks
Edit 1:
#dwirony I've attached three photos: A sample version of WB before using the macro, a sample of an updated WB1, and a sample version of WB after using the macro.

Generate number upon macro execution and transfer it to another table where there is no existing data in another sheet

I am trying to generate a number in A1 in Sheet1 every time the Makro is executed. The number is a combination of the date and a number to be incremented. The final number is to be transferred to Sheet2 in column A where there is not yet data (in this case A1>A2>A3>....)
And this is my attempt in VBA but it does nothing.
Sub incrementInvoiceNumber()
Dim rng_dest As Range
Dim i As Long
Application.ScreenUpdating = False
Sheets("Sheet1").Range("A1").Value = WorksheetFunction.Text(Date, "YYYYMMDD") & WorksheetFunction.Text(Range("A1").Value + 1)
i = 1
Set rng_dest = Sheets("Sheet2").Range("A:A")
Do Until WorksheetFunction.CountA(rng_dest.Rows(i)) = 0
i = i + 1
Loop
Sheets("Sheet2").Range("A" & i).Value = Sheets("Sheet1").Range("A1").Value
Application.ScreenUpdating = True
End Sub
It's not so clear what exactly is making the code not working. I think its that some rages are not called by its parent sheet.
Besides, it's always better to avoid WorksheetFunction if you have VBA function that can do the same thing.
And, finding the last row can be done in a much better way by using End() method.
This is how:
Sub incrementInvoiceNumber()
Dim i As Long
Application.ScreenUpdating = False
Sheets("Sheet1").Range("A1").Value = _
Format(Date, "YYYYMMDD") & Format(Sheets("Sheet1").Range("A1").Value + 1) 'Make sure Sheet1 is the sheet with the value. Fix it if otherwise
With Sheets("Sheet2")
i = .Cells(.Rows.Count, 1).End(xlUp).Row
If Not IsEmpty(.Cells(i, 1)) Then i = i + 1
.Cells(i, 1).Value = Sheets("Sheet1").Range("A1").Value
End With
Application.ScreenUpdating = True
End Sub
Cells(Rows.Count, 1).End(xlUp) means the cell that you will reach if you start from the last cell in column A ("A1048576") and press Ctrl + Up Arrow.

How to copy columns from one worksheet to another on excel with VBA?

I am trying to copy certain column from one worksheet to another but when I apply my code, I get no errors but also no results. I get blank paper. I applied this methodolgy on copying a certain row and it was copied to another worksheet perfectly.
This is regarding the successful attempt to copy row.
The code works just fine:
Sub skdks()
Dim OSheet As Variant
Dim NSheet As Variant
Dim i As Integer
Dim LRow As Integer
Dim NSLRow As Integer
OSheet = "Tabelle3" 'Old Sheet Name
NSheet = "Tabelle5" 'New Sheet Name
LRow = Sheets(OSheet).Cells(Rows.Count, 1).End(xlUp).row 'Last Row in Old Sheet
Sheets(OSheet).Activate
For i = 2 To LRow
'Finds last row in the New Sheet
If Sheets(NSheet).Cells(2, 1) = "" Then
NSLRow = 1
Else
NSLRow = Sheets(NSheet).Cells(Rows.Count, 1).End(xlUp).row
End If
'If cell has "certain # then..."
If Cells(i, 1).Value = Cells(13, 2).Value Then
Cells(i, 1).EntireRow.Copy
Sheets(NSheet).Cells(NSLRow + 1, 1).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
End If
Next i
End Sub
This little piece of code is the failed attempt to copy column to another worksheet.
Sub trial()
Dim OSheet As Variant
Dim NSheet As Variant
Dim j As Integer
Dim LColumn As Integer
Dim NSLColumn As Integer
OSheet = "Tabelle2" 'Old Sheet Name
NSheet = "Tabelle5" 'New Sheet Name
LColumn = Sheets(OSheet).Cells(1, Columns.Count).End(xlToLeft).Column 'Last Column in Old Sheet
Sheets(OSheet).Activate
For j = 2 To LColumn
'Finds last column in the New Sheet
If Sheets(NSheet).Cells(1, 2) = "" Then
NSLColumn = 1
Else
NSLColumn = Sheets(NSheet).Cells(1, Columns.Count).End(xlToLeft).Column
End If
'If cell has "certain # then..."
If Cells(2, j) = Cells(13, 2) Then
Cells(2, j).EntireColumn.Copy
Sheets(NSheet).Cells(2, 2).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
End If
Next j
End Sub
....
'If cell has "certain # then..."
If Cells(2, j) = Cells(13, 2) Then
debug.Print Cells(2, j).Address; " = "; Cells(13, 2).Address; " ---- COPY"
debug.print Cells(2, j).EntireColumn.address; Cells(2, j).EntireColumn.cells.count
debug.Print Sheets(NSheet).Cells(2, 2).Address
Cells(2, j).EntireColumn.Copy
Sheets(NSheet).Cells(2, 2).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
End If
....
With the line If Cells(2, j) = Cells(13, 2) Then you compare the different cells from row 2 (B2, C2, D2, ...) with the value of cell "B13". If the value is the same you copy this column to the new worksheet.
Is there any equal value in your data? If yes you should get an error message with your code.
You try to copy the values of an entire column to the range starting with "B2". Of cause there is not enough space for this.
=> Either you reduce the source range or you start the destination range on row 1!
To add to the paste destination size, if you really want to paste the entire column, you either need to start at the beginning of the column or choose the entire column. Also, I think you want to make the paste column increase with your NSLColumn
If Cells(2, j) = Cells(13, 2) Then
Cells(2, j).EntireColumn.Copy
Sheets(NSheet).Columns(NSLColumn + 1).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
End If

Choose the starting cell of a do-loop

I want to start a loop mid column (Row 15 let's say).
Current code (part of a much larger script)
Range("C2").FormulaR1C1 = "=[OrderForm.xlsx]Order!R15C3"
Dim BlankFound As Boolean
Dim x As Long
'Loop until a blank cell is found in Column C
Do While BlankFound = False
x = x + 1
If Cells(x, "C").Value = "" Then
BlankFound = True
End If
Loop
I tried changing the column ref (C) to a cell (C15). I tried to specify the start and end point (C15:C).
We have a client order form that when they click a button converts to another format ready to be uploaded. The client will fill out various fields that populate rows 1 and 2 (name, address, etc.), then from row three it is the number of orders, i.e.
row
3 part number quantity availability
4 part number quantity availability
I want it to look at the original form and only populate down if it finds a value in the original form's cell.
Then at the end I have another row to add, so I need to be able to say when this loop finishes, add these values (these are just an extra row of totals and some formatting).
The full code-
Sub ButtonMacroLatest()
'Hide alerts
Application.DisplayAlerts = False
'
' Macro8 Macro
'
'Save to users device
ChDir "U:\WINDOWS"
ActiveWorkbook.SaveAs Filename:="U:\WINDOWS\OrderForm.xlsx", FileFormat:= _
xlOpenXMLWorkbook, CreateBackup:=False
'Create new workbook and populate
Workbooks.Add
ActiveCell.FormulaR1C1 = "MSG"
Range("B1").FormulaR1C1 = "=[OrderForm.xlsx]Order!R[1]C"
Range("C1").FormulaR1C1 = "=[OrderForm.xlsx]Order!R[1]C[3]"
Range("D1").FormulaR1C1 = "1400008000"
Range("E1").FormulaR1C1 = "501346009175"
Range("F1").FormulaR1C1 = "=TODAY()"
Range("G1").FormulaR1C1 = "=Now()"
Selection.NumberFormat = "[$-x-systime]h:mm:ss AM/PM"
Range("A2").FormulaR1C1 = "HDR"
Range("B2").FormulaR1C1 = "C"
Range("C2").FormulaR1C1 = "=[OrderForm.xlsx]Order!R4C2"
Range("G2").FormulaR1C1 = "=[OrderForm.xlsx]Order!R[1]C[3]"
Range("H2").FormulaR1C1 = "=[OrderForm.xlsx]Order!R2C4"
Range("K2").FormulaR1C1 = "STD"
Range("L2").FormulaR1C1 = "=[OrderForm.xlsx]Order!R5C2"
Range("N2").FormulaR1C1 = "=[OrderForm.xlsx]Order!R7C2"
Range("O2").FormulaR1C1 = "=[OrderForm.xlsx]Order!R8C2"
Range("Q2").FormulaR1C1 = "=[OrderForm.xlsx]Order!R9C2"
Range("R2").FormulaR1C1 = "=[OrderForm.xlsx]Order!R12C2"
Range("A3").FormulaR1C1 = "POS"
Range("B3").FormulaR1C1 = "=Row()*10-20"
Range("C3").FormulaR1C1 = "=[OrderForm.xlsx]Order!R15C3"
Dim BlankFound As Boolean
Dim x As Long
'Loop until a blank cell is found in Column C
Do While BlankFound = False
x = 14
x = x + 1
If Cells(x, "C").Value = "" Then
BlankFound = True
End If
Loop
Range("D3").FormulaR1C1 = "=[OrderForm.xlsx]Order!R15C1"
Range("E3").FormulaR1C1 = "=[OrderForm.xlsx]Order!R15C2"
Range("F3").FormulaR1C1 = "=[OrderForm.xlsx]Order!R15C5"
Range("G3").FormulaR1C1 = "=[OrderForm.xlsx]Order!R15C7"
'Preformat cells to remove 0 value
Range("A1:AP1000").Select
Range("AP1000").Activate
Selection.NumberFormat = "#;#;"
Range("H3").FormulaR1C1 = "GBP"
Range("L3").FormulaR1C1 = "TRA"
Range("M3").FormulaR1C1 = "=COUNTIF(C[-3], ""POS"")+COUNTIF(C[-3], ""HDR"")"
'Reinstate alerts
Application.DisplayAlerts = True
End Sub
In the client facing form A15:C15 are material/part numbers. If populated those rows should fill down in the new form until there is no entry in the original form.
Customer form
I haven't been able to figure out exactly where you're grabbing values from and where you're putting them, but hopefully this bit of code will give you enough ideas to get yours sorted.
Public Sub ButtomMacroLatest()
Dim wrkBk As Workbook
Dim wbOF As Workbook
Dim shtCSV As Worksheet
Dim shtOF As Worksheet
Dim lLastRow As Long
Dim x As Long, y As Long
'OrderForm is closed so needs opening:
'Set wbOF = Workbooks.Open("U:\.......\OrderForm.xlsx")
'OrderForm is the workbook containing this code:
Set wbOF = ThisWorkbook
'Set a reference to the "Order" sheet and
'find the last row - based on column A being populated.
Set shtOF = wbOF.Worksheets("Order")
lLastRow = shtOF.Cells(Rows.Count, 1).End(xlUp).Row
'Create workbook with 1 sheet and set reference to that sheet.
Set wrkBk = Workbooks.Add(xlWBATWorksheet)
Set shtCSV = wrkBk.Worksheets(1)
'Add headings to the sheet.
shtCSV.Range("A1:G1") = Array("MSG", "SomeHeading", "SomeOtherHeading", "1400008000", _
"501346009175", Date, Now)
'Copy values in cell "A15:J<LastRow>" to "A2" on the new sheet.
With shtOF
'Straight copy
'.Range(.Cells(15, 1), .Cells(lLastRow, 10)).Copy _
Destination:=shtCSV.Range("A2")
'Paste Special
.Range(.Cells(15, 1), .Cells(lLastRow, 10)).Copy
With shtCSV.Range("A2")
.PasteSpecial xlPasteValuesAndNumberFormats
.PasteSpecial xlPasteFormats
End With
'Make the value of one cell equal the value of another cell
'in a loop from row 15 to LastRow and column 1 to 10.
'For x = 15 To lLastRow
' For y = 1 To 10
' shtCSV.Cells(x - 13, y) = .Cells(x, y)
' Next y
'Next x
End With
wrkBk.SaveAs Environ("temp") & "/CSV File.csv", FileFormat:=xlCSV, CreateBackup:=False
End Sub
This took something a lot simpler, it works a treat for what I need. Code:
'Fills column to last row of data from Cell C15
Dim LastRow As Long
LastRow = Cells.Find(What:="*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Range("C15:C" & LastRow).FillDown
Range("D15:D" & LastRow).FillDown
Range("E15:E" & LastRow).FillDown
Thanks for all of the responses.

If Condition to create sheets only when Auto filter has data

I have written a code which does the below steps.
1) Loops through a list of products
2) Auto filters the data with each product.
3) Copies and pastes data on to separate worksheets and names it with that product name.
4) Inserts a line at every change in schedule
The only thing I couldn't do it here is to limit separate worksheet creation only for the products available in the source data when auto filtered.
I tried to do this by adding an if condition to add worksheets by product name only if auto filter shows any data but for some reason it is not working.
I would appreciate any help in fixing this problem and clean my code to make it look better and work faster.
Sub runreport()
Dim rRange As Range
Dim Rng As Range
' Open the Source File
Filename = Application.GetOpenFilename()
Workbooks.Open Filename
'Loops through each product type range from the macro spreadsheet.
For Each producttype In ThisWorkbook.Sheets("Schedule").Range("Product")
' Filters the sheet with a product code that matches and copy's the active sheet selection
Workbooks("Source.xlsx").Sheets("Sheet1").Range("A1:G1").AutoFilter Field:=4, Criteria1:=producttype
Sheets("Sheet1").Select
Sheets("Sheet1").Select
Range("A2").Select
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy
'Adds a new workbook
ActiveWorkbook.Sheets.Add After:=ActiveWorkbook.Sheets(Sheets.Count)
'Names the worksheet by Prod type descreption doing a vlookup from the spreadsheet
ActiveSheet.Name = Application.VLookup(producttype, ThisWorkbook.Sheets("Sheet2").Range("A:B"), 2, False)
'This will paste the filtered data from Source Data to the new sheet that is added
Range("a2").Select
ActiveSheet.Paste
ns = ActiveSheet.Name
'Copeis the headers to all the new sheets
Sheets("Sheet1").Select
Range("A1:BC1").Select
Selection.Copy
Sheets(ns).Activate
Range("a1").Select
ActiveSheet.Paste
Columns.AutoFit
' Inserts a blank row for everychange in ID
myRow = 3
Do Until Cells(myRow, 3) = ""
If Cells(myRow, 3) = Cells(myRow - 1, 3) Then
myRow = myRow + 1
Else
Cells(myRow, 1).EntireRow.Insert
myRow = myRow + 2
End If
Loop
Next producttype
End Sub
Try this...
Sub runreport()
Dim rRange As Range
Dim Rng As Range
Dim FiltRows As Integer
' Open the Source File
Filename = Application.GetOpenFilename()
Workbooks.Open Filename
'Loops through each product type range from the macro spreadsheet.
For Each producttype In ThisWorkbook.Sheets("Schedule").Range("Product")
' Filters the sheet with a product code that matches and copy's the active sheet selection
Workbooks("Source.xlsx").Sheets("Sheet1").Range("A1:G1").AutoFilter Field:=4, Criteria1:=producttype
With Workbooks("Source.xlsx").Sheets("Sheet1")
FiltRows = .AutoFilter.Range.Rows.SpecialCells(xlCellTypeVisible).Count / .AutoFilter.Range.Columns.Count
End With
If FiltRows > 1 Then 'There will always be a header row which is why it needs to be greater than one.
Sheets("Sheet1").Select
Sheets("Sheet1").Select
Range("A2").Select
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy
'Adds a new workbook
ActiveWorkbook.Sheets.Add After:=ActiveWorkbook.Sheets(Sheets.Count)
'Names the worksheet by Prod type descreption doing a vlookup from the spreadsheet
ActiveSheet.Name = Application.VLookup(producttype, ThisWorkbook.Sheets("Sheet2").Range("A:B"), 2, False)
'This will paste the filtered data from Source Data to the new sheet that is added
Range("a2").Select
ActiveSheet.Paste
ns = ActiveSheet.Name
'Copeis the headers to all the new sheets
Sheets("Sheet1").Select
Range("A1:BC1").Select
Selection.Copy
Sheets(ns).Activate
Range("a1").Select
ActiveSheet.Paste
Columns.AutoFit
' Inserts a blank row for everychange in ID
myRow = 3
Do Until Cells(myRow, 3) = ""
If Cells(myRow, 3) = Cells(myRow - 1, 3) Then
myRow = myRow + 1
Else
Cells(myRow, 1).EntireRow.Insert
myRow = myRow + 2
End If
Loop
End If
Next producttype
End Sub
I would recommend you define more variables than you have it keeps the code cleaner and easier to read as well as eliminates easy errors.
I also recommend always to utilize "option explicit" at the top of every code. It forces defining all variables (when you don't define a variable the program will do it for you (assuming you haven't used option explicit), but excel doesn't always get it correct. Also option explicit helps you avoid typos in variables.
Also as a general rule you rarely if ever have to .select anything to do what you need to with vba.
Below is an example of a cleaned up and shortened code which utilized variable definition and instantiation.
Sub runreport()
Dim wb As Workbook
Dim wsSched As Worksheet
Dim wsNew As Worksheet
Dim wbSource As Workbook
Dim wsSource As Worksheet
Dim rRange As Range
Dim producttype As Range
Dim Filename As String
Dim FiltRows As Integer
Dim myRow As Integer
'instantiate Variables
Set wb = ThisWorkbook
Set wsSched = wb.Worksheets("Schedule")
' Open the Source File
Filename = Application.GetOpenFilename()
Set wbSource = Workbooks.Open(Filename)
Set wsSource = wbSource.Worksheets("Sheet1")
'Loops through each product type range from the macro spreadsheet.
For Each producttype In wsSched.Range("Product")
' Filters the sheet with a product code that matches and copy's the active sheet selection
With wsSource
.AutoFilterMode = False
.Range("A1:G1").AutoFilter Field:=4, Criteria1:=producttype
FiltRows = .AutoFilter.Range.Rows.SpecialCells(xlCellTypeVisible).Count / .AutoFilter.Range.Columns.Count
If FiltRows > 1 Then 'There will always be a header row which is why it needs to be greater than one.
'Add new workbook
Set wsNew = wb.Sheets.Add(After:=ActiveWorkbook.Sheets(Sheets.Count))
'Copy filtered data including header
.AutoFilter.Range.SpecialCells(xlCellTypeVisible).Copy
'Paste filterd data and header
wsNew.Range("A1").PasteSpecial
Application.CutCopyMode = False
wsNew.Columns.AutoFit
'Rename new worksheet
wsNew.Name = WorksheetFunction.VLookup(producttype, wb.Worksheets("Sheet2").Range("A:B"), 2, False)
' Inserts a blank row for everychange in ID
myRow = 3
Do Until Cells(myRow, 3) = ""
If Cells(myRow, 3) = Cells(myRow - 1, 3) Then
myRow = myRow + 1
Else
Cells(myRow, 1).EntireRow.Insert
myRow = myRow + 2
End If
Loop
End If
End With
Next producttype
End Sub
First, you can check this answer for ways to optimize your vba code
As for your code in its current form, it would be easiest if you select the entire range of your product code data first. Then you can check this range after your filter and determine if all the rows are hidden. See a sample of the code below
Dim productData as Range
Set productData = Range(Range("A2"), Range("A2").End(xlDown).End(xlToRight))
' Filters the sheet with a product code that matches and copy's the active sheet selection
Workbooks("Source.xlsx").Sheets("Sheet1").Range("A1:G1").AutoFilter _
Field:=4, Criteria1:=producttype
' The error check will skip the creation of a new sheet if the copy failed (i.e. returns a non-zero error number)
On Error Resume Next
' Copies only the visible cells
productData.SpecialCells(xlCellTypeVisible).Copy
If Err.number = 0 then
'Adds a new workbook
ActiveWorkbook.Sheets.Add After:=ActiveWorkbook.Sheets(Sheets.Count)
ActiveSheet.Name = Application.VLookup(producttype, _
ThisWorkbook.Sheets("Sheet2").Range("A:B"), 2, False)
Range("a2").Select
ActiveSheet.Paste
End If
While you can Range.Offset one row and check if the Range.SpecialCells method with xlCellTypeVisible is Not Nothing, I prefer to use the worksheet's SUBTOTAL function. The SUBTOTAL function discards hidden or filtered rows from its operations so a simple COUNTA (SUBTOTAL subfunction 103) of the cells below the header will tell you if there is anything available.
Sub runreport()
Dim rRange As Range, rHDR As Range, rVAL As Range, wsn As String
Dim fn As String, owb As Workbook, twb As Workbook
Dim i As Long, p As Long, pTYPEs As Variant
pTYPEs = ThisWorkbook.Sheets("Schedule").Range("Product").Value2
Set twb = ThisWorkbook
' Open the Source File
fn = Application.GetOpenFilename()
Set owb = Workbooks.Open(fn)
With owb
'is this Workbooks("Source.xlsx")?
End With
With Workbooks("Source.xlsx").Worksheets("Sheet1")
With .Cells(1, 1).CurrentRegion
'store the header in case it is needed for a new worksheet
Set rHDR = .Rows(1).Cells
'reset the the filtered cells
Set rVAL = Nothing
For p = LBound(pTYPEs) To UBound(pTYPEs)
.AutoFilter Field:=4, Criteria1:=pTYPEs(p)
With .Resize(.Rows.Count - 1, 7).Offset(1, 0) '<~~resize to A:G and move one down off the header row
If CBool(Application.Subtotal(103, .Cells)) Then
'there are visible cells; do stuff here
Set rVAL = .Cells
wsn = Application.VLookup(pTYPEs(p), twb.Worksheets("Sheet2").Range("A:B"), 2, False)
'if the wsn worksheet doesn't exist, go make one and come back
On Error GoTo bm_New_Worksheet
With Worksheets(wsn)
On Error GoTo bm_Safe_Exit
rVAL.Copy Destination:=.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)
'when inserting rows, always work from the bottom to the top
For i = .Cells(Rows.Count, 3).End(xlUp).Row To 3 Step -1
If .Cells(i, 3).Value2 <> .Cells(i - 1, 3).Value2 Then
.Rows(i).Insert
End If
Next i
'autofit the columns
For i = .Columns.Count To 1 Step -1
.Columns(i).AutoFit
Next i
End With
End If
End With
Next p
End With
End With
GoTo bm_Safe_Exit
bm_New_Worksheet:
On Error GoTo 0
With Worksheets.Add(after:=Sheets(Sheets.Count))
.Name = wsn
rHDR.Copy Destination:=.Cells(1, 1)
End With
Resume
bm_Safe_Exit:
End Sub
When a worksheet that is referenced by the wsn string does not exist, the On Error GoTo bm_New_Worksheet runs off and creates one. The Resume brings the code processing right back to the place it errored out.
One caution when using this method is to ensure that you have unique, legal worksheet names returned by your VLOOKUP function.

Resources