vba copy corresponding values from another workbook? - vba

I have two workbooks:
Planner
Column K Column AG
123 £100
246 £20
555 £80
Master
Column D Column R
123 £100
246 £20
555 £80
I am trying to copy the values from Planner, Column AG into Column R (Master) where my item numbers in Column D (Master) match with column K (Planner).
My code below produces no error and it is not producing any results - despite their being several matches.
Please can someone show me where i am going wrong?
For the avoidance of doubt, my workbook is definitely opening ok so is finding the file.
Code:
Sub PlannerOpen()
'Set Variables
Dim wb2 As Workbook
Dim i As Long
Dim j As Long
Dim lastRow As Long
Dim app As New Excel.Application
'Find Planner
If Len(FindDepotMemo) Then
'If Found Then Set Planner Reference.
app.Visible = False 'Visible is False by default, so this isn't necessary
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Application.EnableEvents = False
Set wb2 = Workbooks.Open(FindDepotMemo, ReadOnly:=True, UpdateLinks:=False)
'If We have our planner lets continue...
'With my workbook
With wb2.Worksheets(1)
lastRow = .Cells(.Rows.Count, "K").End(xlUp).Row
'Lets begin our data merge
j = 2
For i = 2 To lastRow
'If data meets criteria
'Check Planner For Turnover
If ThisWorkbook.Worksheets("Data").Range("D" & j).Value = .Range("K" & i).Value Then ' check if Item number matches
ThisWorkbook.Worksheets("Data").Range("R" & j).Value = .Range("AG" & i).Value
j = j + 1
End If
'Continue until all results found
Next i
End With
'All Done, Let's tidy up
'Close Workbooks
'wb2.Close SaveChanges:=False
'app.Quit
'Set app = Nothing
Application.DisplayAlerts = True
Application.ScreenUpdating = True
Application.EnableEvents = True
End If
End Sub
Function FindDepotMemo() As String
Dim Path As String
Dim FindFirstFile As String
Path = "G:\BUYING\Food Specials\2. Planning\1. Planning\1. Planner\" & "8." & " " & Year(Date) & "\"
FindFirstFile = Dir$(Path & "*.xlsx")
While (FindFirstFile <> "")
If InStr(FindFirstFile, "Planner") > 0 Then
FindDepotMemo = Path & FindFirstFile
Exit Function
End If
FindFirstFile = Dir
Wend
End Function

Instead of having 2 For loops, just use the Application.Match to find matches between values in your 2 workbooks.
Use this code section below to replace with yours:
With wb2.Worksheets(1)
Dim MatchRow As Variant '<-- define variable to get the row number if Match is successful
lastRow = .Cells(.Rows.Count, "K").End(xlUp).Row
'Lets begin our data merge
For i = 2 To lastRow
' If data meets criteria
' Check Planner For Turnover
' Use Application.Match to find matching results between workbooks
If Not IsError(Application.Match(ThisWorkbook.Worksheets("Data").Range("D" & i).Value, .Range("K2:K" & lastorw), 0)) Then ' check if Match is successful
MatchRow = Application.Match(ThisWorkbook.Worksheets("Data").Range("D" & i).Value, .Range("K2:K" & lastorw), 0) ' <-- get the row number where the match was found
ThisWorkbook.Worksheets("Data").Range("R" & j).Value = .Range("AG" & MatchRow).Value
End If
'Continue until all results found
Next i
End With

you could refactor your code as follows:
Option Explicit
Sub PlannerOpen()
Dim dataRng As Range, cell As Range
Dim depotMemo As String
Dim iRow As Variant
If FindDepotMemo(depotMemo) Then '<--| if successfully found the wanted file
With ThisWorkbook.Worksheets("Data1") '<--| reference your "Master" workbook relevant worksheet
Set dataRng = .Range("D2", .Cells(.Rows.Count, "D").End(xlUp)) '<--| set its item numbers range
End With
With Workbooks.Open(depotMemo, ReadOnly:=True, UpdateLinks:=False).Worksheets(1) '<--| open depotMemo workbook and reference its first worksheet
For Each cell In .Range("K2", .Cells(.Rows.Count, "K").End(xlUp)) '<--| loop through referenced worksheet column "K" cells from row 2 down to last not empty one
iRow = Application.Match(cell.Value, dataRng, 0) '<--| try finding current depotMemo item number in Master item numbers range
If Not IsError(iRow) Then dataRng(iRow, 1).Offset(, 14).Value = cell.Offset(, 22) '<--| if found then grab depotMemo current item amount and place it in corresponding "master" data sheet column R
Next
.Parent.Close False
End With
End If
End Sub
Function FindDepotMemo(depotMemo As String) As Boolean
Dim Path As String
Dim FindFirstFile As String
Path = "G:\BUYING\Food Specials\2. Planning\1. Planning\1. Planner\" & "8." & " " & Year(Date) & "\"
FindFirstFile = Dir$(Path & "*.xlsx")
While (FindFirstFile <> "")
If InStr(FindFirstFile, "Planner") > 0 Then
FindDepotMemo = True
depotMemo = Path & FindFirstFile
Exit Function
End If
FindFirstFile = Dir
Wend
End Function

Related

VBA matching and update existing and add new field into Next Empty Cell on next row

I'm new here and I am trying to do a VBA Vlookup function.
My objectives is to VLookup Table1 from Sheet 1 to Table2 from Sheet2 using Column A and update column B and C if A is existing.
If A is not existing to add into Table1 next blank row with column B and C also included.
Please refer below image - expectation of Sheet1 with updated results.
Thank you in advance.
Currently able to code only to update existing fields but not sure how to add fields that does not match into next blank row of Sheet1.
Sub getOpenExcel()
' Your daily report has a date in it's name
' to select an open workbook we must first know it's name
' AND - it must be already open
' Your examples are 2017-03-11-18875, 2017-03-12-18875, 2017-03-13-18875
' If the name is the current date then this would work to get the filename
Dim fileName As String, monthNum As String, dayNum As String, wb1 As Workbook, wb2 As Workbook
Dim ws1 As Worksheet, ws2 As Worksheet, rng1 As Range, rng2 As Range
' this adds a ZERO to the front of month numbers less than 10
If Month(Date) < 10 Then
monthNum = "0" & CStr(Month(Date))
Else
monthNum = CStr(Month(Date))
End If
' You may or may not need this section
' it adds a ZERO to the front of day numbers less than 10
If Day(Date) < 10 Then
dayNum = "0" & CStr(Day(Date))
Else
dayNum = CStr(Day(Date))
End If
' many cases the daily report will come from the previous day
' If your file has yesterday's date, then comment out the above code and
'uncomment the following code
'
'If Day(DateAdd("d", -1, Date)) < 10 Then
' dayNum = "0" & Day(DateAdd("d", -1, Date))
'Else
' dayNum = Day(DateAdd("d", -1, Date))
'End If
fileName = "GREENBILL_RECON_DETAILED_REPORT_" & CStr(Year(Date)) & monthNum & dayNum
' if today's date is 3/14/17 then "fileNem" = "2017-03-12-18875"
' If your daily report is an excel book, then we need to add the proper extension.
' It could be one of many, "xls", ".xlsx" , ".xlsm", etc....
' If your daily report is open - look at the top. It should have the file name and extension.'
' Replace the below extension with the correct one.
fileName = fileName & ".csv"
' Again, if today's date is 3/14/17 then "fileNem" = "2017-03-12-18875.xlsx"
' This is where we set both workbooks to variables
'
Set wb1 = ThisWorkbook
Set ws1 = wb1.Worksheets("Sheet1")
On Error GoTo notOpen
Set wb2 = Workbooks(fileName) ' This is your daily report
On Error GoTo 0
Set ws2 = wb2.Worksheets("GREENBILL_RECON_DETAILED_REPORT")
ws1.Activate
'*************************************************************************************
' If successful this is the area where you put your code to copy and paste automatically '
' If you need this pasted to the first empty row at bottom of page then 'put code here to find the first empty row and use that varaible
' with range("a" & firstUnusedRow) intstead of A1 ...
wb2.Activate
Range("A1:Z500").Copy _
Destination:=wb1.Worksheets("Sheet1").Range("A1") 'change A1 to A &
firstUnusedRow
'*************************************************************************************
' This is the clean up and exit code
Set wb1 = Nothing
Set wb2 = Nothing
Exit Sub
notOpen:
On Error GoTo 0
Set wb1 = Nothing
MsgBox "The file " & fileName & " is not open"
Exit Sub
End Sub
Sub Rectangle3_Click()
On Error Resume Next
Dim Dept_Row As Long ' To Change to Billing_Acc
Dim Dept_Clm As Long ' To Change to Org_Seqno
Table1 = Sheet1.Range("A1:A10") ' Input file name
Table2 = Sheet2.Range("A1:B10") ' Range of table
Dept_Row = Sheet1.Range("B1").Row
Dept_Clm = Sheet1.Range("B1").Column
For Each cl In Table1
Sheet1.Cells(Dept_Row, Dept_Clm) = Application.WorksheetFunction.VLookup(cl, Table2, 2, False)
Dept_Row = Dept_Row + 1
Next cl
MsgBox "Done"
End Sub
Below code is done iterating thru all the rows by matching column A of both sheets. If not found this will add a new line in sheet1.
Dim lngRow1, lngRow2 As Long
lngRow1 = Sheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
lngRow2 = Sheets("Sheet2").Cells(Rows.Count, 1).End(xlUp).Row
Dim isFound As Boolean
Dim lastRow As Long
Dim i, j As Long
lastRow = lngRow1
For i = 1 To lngRow2
isFound = False
For j = 1 To lngRow1
If Sheets("Sheet1").Range("A" & i) = Sheets("Sheet2").Range("A" & j) Then
Sheets("Sheet1").Range("B" & i) = Sheets("Sheet2").Range("B" & j)
Sheets("Sheet1").Range("C" & i) = Sheets("Sheet2").Range("C" & j)
isFound = True
End If
Next j
If Not isFound Then
lastRow = lastRow + 1
Sheets("Sheet1").Range("A" & lastRow) = Sheets("Sheet2").Range("A" & i)
Sheets("Sheet1").Range("B" & lastRow) = Sheets("Sheet2").Range("B" & i)
Sheets("Sheet1").Range("C" & lastRow) = Sheets("Sheet2").Range("C" & i)
End If
Next i
Code written considering the above example image. if number of columns are different than the example please modify the code accordingly.

Excel VBA skips a lot of occurrences

I have a Workbook with 6 Sheets. I am walking through them with For Each. And the task is:
1) Walk though every cell with specified Range
2) If cell is not empty AND contains ONLY number THEN add to the end of the cell " мм". Otherwise SKIP this cell.
But in fact, script does it good only for first sheet (Worksheet). It does no changes to other sheets. I don't know why this happens. I think, that there is some error or mistake in the code, but I double-checked it and everything seems to be correct. Help me please :)
Sub SaveWorksheetsAsCsv()
Dim xWs As Worksheet
Dim xDir As String
Dim folder As FileDialog
Dim r As Range
Dim rr As Range
Dim rrrrrr As Range
Dim cell As Range
k = Cells(Rows.Count, "A").End(xlUp).Row
Set folder = Application.FileDialog(msoFileDialogFolderPicker)
If folder.Show <> -1 Then Exit Sub
xDir = folder.SelectedItems(1)
For Each xWs In Application.ActiveWorkbook.Worksheets
If xWs.Name Like "Worksheet" Then
Set r = Range("FA2:FA" & k)
For Each cell0 In r
If IsEmpty(cell0.Value) = False And IsNumeric(cell0.Value) = True Then
cell0.Value = cell0.Value & " мм"
End If
Next
'xWs.Columns(41).EntireColumn.Delete
End If
If xWs.Name Like "Worksheet 1" Then
Set rr = Range("AG2:AG" & k)
For Each cell1 In rr
If IsEmpty(cell1.Value) = False And IsNumeric(cell1.Value) Then
cell1.Value = cell1.Value & " мм"
End If
Next
'xWs.Columns(126).EntireColumn.Delete
End If
If xWs.Name Like "Worksheet 5" Then
Set rrrrrr = Range("FR2:FR" & k)
For Each cell5 In rrrrrr
If IsEmpty(cell5.Value) = False And IsNumeric(cell5.Value) Then
cell5.Value = cell5.Value & " мм"
End If
Next
End If
xWs.SaveAs xDir & "\" & xWs.Name, xlCSV, local:=True
Next
End Sub
These sets of statements need to be adjusted to correct sheet references. Current code will always look at active sheet and the range reference is not qualified.
Set r = Range("FA2:FA" & k)
Set r = xWs.Range("FA2:FA" & k)
You can shorten-up and utilize your code a lot.
First, your k = Cells(Rows.Count, "A").End(xlUp).Row trying to get the last row, needs to be inside the For Each xWs In Application.ActiveWorkbook.Worksheets , since the last row will be different for each worksheet.
Second, instead of multiple Ifs, you can use Select Case.
Third, there is no need to have 3 different objects for Range, like r, rr, and rrr. The same goes for cell0, cell1 and cell5, you can use just one r and cell.
The only thing different inside your If (my Select Case) is the range you set r. The rest, looping through r.Cells is the same for all 3 criterias, so you can take this part outside the loop, and have it only once.
Modifed Code
Option Explicit
Sub SaveWorksheetsAsCsv()
Dim xWs As Worksheet
Dim xDir As String
Dim folder As FileDialog
Dim r As Range
Dim cell As Range
Dim k As Long
Set folder = Application.FileDialog(msoFileDialogFolderPicker)
If folder.Show <> -1 Then Exit Sub
xDir = folder.SelectedItems(1)
For Each xWs In ThisWorkbook.Worksheets ' it's safer to use ThisWorkbook is you reffer to the worksheets inside the workbook which thid code resides
With xWs
' getting the last row needs to be inside the loop
k = .Cells(.rows.Count, "A").End(xlUp).Row
Set r = Nothing ' reset Range Object
Select Case .Name
Case "Worksheet"
Set r = .Range("FA2:FA" & k)
'xWs.Columns(41).EntireColumn.Delete
Case "Worksheet 1"
Set r = .Range("AG2:AG" & k)
'xWs.Columns(126).EntireColumn.Delete
Case "Worksheet 5"
Set r = .Range("FR2:FR" & k)
End Select
' check if r is not nothing (it passed one of the 3 Cases in the above select case)
If Not r Is Nothing Then
For Each cell In r
If IsEmpty(cell.Value) = False And IsNumeric(cell.Value) Then
cell.Value = cell.Value & " мм"
End If
Next cell
End If
.SaveAs xDir & "\" & .Name, xlCSV, Local:=True
End With
Next xWs
End Sub

Excel vba count last row source file

I am relatively new to VBA.
I have a target workbook where the first step is somebody has to write something. If not an error message pops up.
After they filled in their data the code will ask them to open the source workbook(they downloaded the file before). From the source workbook certain columns are read. This all works fine in the code. The problem I have is to get what they filled in the target workbook to be copied down in column (A4:A(last row in source workbook). So the length until where their manually entered data has to be copied down has to be equal to the length of data in the source workbook.
Sub get_rate_codes()
Dim CheckCell As Range
Dim wb_source As Workbook
Dim wb_target As Workbook
Dim strPathName As String
Dim lastRow As Long
For Each CheckCell In Sheets("rate_codes").Range("F3").Cells
If Len(Trim(CheckCell.Value)) = 0 Then
CheckCell.Select
MsgBox "Cell " & CheckCell.Address(0, 0) & " is empty. Please enter SITA."
Exit Sub
End If
Next CheckCell
'start to open file
Application.ScreenUpdating = False
'start is the starting cell while lastRow measures the last data row in the external file
Start = 4
'continue to copy data from the rate codes report
Set wb_target = ActiveWorkbook
With wb_target.Sheets("rate_codes")
lastRow = wb_source.UsedRange.SpecialCells(xlCellTypeLastCell).Row
strPathName = Application.GetOpenFilename()
If strPathName = "False" Then
Exit Sub
End If
Set wb_source = Workbooks.Open(strPathName, 0)
.Range("B" & Start & ":B1000").Value = wb_source.Sheets(1).Range("E2:E1000").Value
.Range("C" & Start & ":C1000").Value = wb_source.Sheets(1).Range("H2:H1000").Value
.Range("D" & Start & ":D1000").Value = wb_source.Sheets(1).Range("G2:G1000").Value
.Range("E" & Start & ":E1000").Value = wb_source.Sheets(1).Range("K2:K1000").Value
.Range("A" & Start & ":A" & lastRow).Value = wb_target.Sheets(2).Range("F2").Value '
wb_source.Close (False)
End With
'close file without saving
Application.ScreenUpdating = True
End Sub
To find the last row you can do this.
Dim lastRow As Long
lastRow = wb_source.Cells(wb_source.Rows.count, "A").End(xlUp).Row
Then you can write to the next line.
.Range("A" & lastRow + 1).Value =

I need to create new sheets based on unique names found in column A. Current Code generates excess data in certain sheets

I have the following code so far based on questions asked by other people.
I have a set of names listed in column A, and 216 columns and 9725 rows of data.
Currently using the following code I get the new sheets created except along with the unique names and its relevant data I get many cells filled with "#N/A".
In certain cases, the name Bob for example will be populated in a new sheet called Bob but the first column will have Bob and all relevant data and once all Bobs rows are shown it is follower with many rows with #N/A and all columns with #N/A.
In other cases the sheet will be created for Charles and all of Charles data will be listed, then many rows of #N/A and then all of the master-data including other peoples names which I need to avoid.
I want each individual sheet to only have the info based on the name of the person on that sheet. All of the data gets copied as I verified the number of accurate cells that get populated yet I get these #N/A cells and duplicated extra data and I'm not sure how to stop it from being populated? Any help in cleaning the code would be appreciated!!
Code:
Sub CopyDataFromReportToIndividualSheets()
Dim ws As Worksheet
Set ws = Sheets("FormulaMSheet2")
Dim LastRow As Long
Dim MyRange As Range
Worksheets("FormulaMSheet2").Activate
LastRow = Range("A" & ws.Rows.Count).End(xlUp).Row
' stop processing if we don't have any data
If LastRow < 2 Then Exit Sub
Application.ScreenUpdating = False
' SortMasterList LastRow, ws
CopyDataToSheets LastRow, ws
ws.Select
Application.ScreenUpdating = True
End Sub
Sub SortMasterList(LastRow As Long, ws As Worksheet)
ws.Range("A2:BO" & LastRow).Sort Key1:=ws.Range("A1")
', Key2:=ws.Range("B1")
End Sub
Sub CopyDataToSheets(LastRow As Long, src As Worksheet)
Dim allAgentNameCells As Range
Dim cell As Range
Dim Series As String
Dim SeriesStart As Long
Dim SeriesLast As Long
Set allAgentNameCells = Range("A2:A" & LastRow)
SeriesStart = 2
Series = Range("A" & SeriesStart).Value
For Each cell In allAgentNameCells
If cell.Value <> " " And cell.Value <> "" Then
' Condition ` And cell.Value <> "" ` added for my testdata. If you don't need this, please remove.
' Current Row's Series not SPACE
If cell.Value <> Series Then
SeriesLast = cell.Row - 1
CopySeriesToNewSheet src, SeriesStart, SeriesLast, Series
Series = cell.Value
SeriesStart = cell.Row
End If
End If
Next
'' copy the last series
SeriesLast = LastRow
CopySeriesToNewSheet src, SeriesStart, SeriesLast, Series
End Sub
Sub CopySeriesToNewSheet(src As Worksheet, Start As Long, Last As Long, name As String)
Dim tgt As Worksheet
Dim MyRange As Range
If (SheetExists(name)) Then
MsgBox "Sheet " & name & " already exists. " _
& "Please delete or move existing sheets before" _
& " copying data from the Master List.", vbCritical, _
"Time Series Parser"
End
Else
If Series = " " Then
End
End If
End If
Worksheets("FormulaMSheet2").Activate
' Worksheets.Add(after:=Worksheets(Worksheets.Count)).name = name
Worksheets("FormulaMSheet2").Copy After:=Worksheets(Worksheets.Count)
ActiveSheet.name = name
Set tgt = Sheets(name)
' copy data from src to tgt
tgt.Range("A2:BO2" & Last - Start + 2).Value = src.Range("A" & Start & ":BO" & Last).Value
End Sub
Function SheetExists(name As String) As Boolean
Dim ws As Variant
For Each ws In ThisWorkbook.Sheets
If ws.name = name Then
SheetExists = True
Exit Function
End If
Next
SheetExists = False
End Function
You need replace the
tgt.Range("A2:BO2" & Last - Start + 2).Value = src.Range("A" & Start & ":BO" & Last).Value
to
src.Range("A" & Start & ":BO" & Last).SpecialCells(xlCellTypeVisible).Copy Destination:=tgt.Range("A2:BO2" & Last - Start + 2)
I found what I needed at the following site: http://www.rondebruin.nl/win/s3/win006_5.htm .
I figured if anyone else was looking for similar code it would help taking a look at the site.

Excel Vba: Creating loop that checks if the values in column A matches and copy all the rows to a new spreadsheet

I need to select all the rows in column A that have the same the value and paste them to a new spreadsheet named with the copied name.
In the example picture when I run macro and input value Banana I should get all the rows that contain banana in column A.
I found following vba code from the internet and tried to modify it to my needs but I'm stuck:
Sub LookForAllSameValues()
Dim LSearchRow As Integer
Dim LCopyToRow As Integer
On Error GoTo Err_Execute
'Start search in row 4
LSearchRow = 2
'Start copying data to row 2 in Sheet2 (row counter variable)
LCopyToRow = 2
Uname = InputBox("Test")
ActiveWorkbook.Worksheets.Add.Name = Uname
While Len(Range("A" & CStr(LSearchRow)).Value) > 0
'If value in column E = "Mail Box", copy entire row to Sheet2
If Range("A" & CStr(LSearchRow)).Value = Uname Then
'Select row in Sheet1 to copy
Rows(CStr(LSearchRow) & ":" & CStr(LSearchRow)).Select
Selection.Copy
'Paste row into Sheet2 in next row
Sheets(Uname).Select
Rows(CStr(LCopyToRow) & ":" & CStr(LCopyToRow)).Select
ActiveSheet.Paste
'Move counter to next row
LCopyToRow = LCopyToRow + 1
'Go back to Sheet1 to continue searching
Sheets("Sheet1").Select
End If
LSearchRow = LSearchRow + 1
Wend
'Position on cell A3
Application.CutCopyMode = False
Range("A3").Select
MsgBox "All matching data has been copied."
Exit Sub
Err_Execute:
MsgBox "An error occurred."
End Sub
This code almost works. It asks user to input string to search and then it creates a new worksheet named as this one. The problem lies in the loop, I debugged the code and for some reason it just skips copy paste loop
How do I get the loop working?
Output when the code is run:
I'm assuming you're testing this on the data shown above.
Your code states that LSearch Row = 2 and therefore your search will begin in cell A2. I'd therefore assume your loop is never executing because Len(Range("A2")) equals 0 (the cell is empty) and the loop immediately exits. This also means that if any cell in column A is empty the loop will end there even if there is more data below it.
Instead try using a For..Next loop as shown below which will run from row 2 to the last used row in the active sheet, regardless of the cell contents.
Public Sub FindAndCreateNew()
Dim strFind As String
Dim i As Long, j As Long
Dim wsFind As Worksheet
Dim wsPaste As Worksheet
'Get value to search for
strFind = InputBox("Test")
'Create object reference to the current worksheet
Set wsFind = ActiveSheet
'Create a new worksheet with object reference and then rename it
Set wsPaste = Worksheets.Add
wsPaste.Name = strFind
'Paste starting at row 2 in wsPaste
j = 2
'Start searching from row 2 of wsFind, continue to end of worksheet
For i = 2 To wsFind.UsedRange.Rows.Count
If wsFind.Range("A" & i) = strFind Then
'Copy row i of wsFind to row j of wsPaste then increment j
wsFind.Range(i & ":" & i).Copy Destination:=wsPaste.Range(j & ":" & j)
j = j + 1
End If
Next i
End Sub
P.S. It's also worth noting that the use of .Select is generally avoidable and it can slow the program down considerably as well as making it less readable. For example this:
'Select row in Sheet1 to copy
Rows(CStr(LSearchRow) & ":" & CStr(LSearchRow)).Select
Selection.Copy
Could be represented with just one statement as below:
'Select row in Sheet1 to copy
Rows(CStr(LSearchRow) & ":" & CStr(LSearchRow)).Copy
As commented, try this:
Sub test()
Dim sh1 As Worksheet, sh2 As Worksheet
Dim rng As Range
Dim uname As String
Set sh1 = Sheet1: uname = InputBox("Input")
With Application
.ScreenUpdating = False
.DisplayAlerts = False
End With
If Len(uname) = 0 Then MsgBox "Invalid input": Exit Sub
Set sh2 = ThisWorkbook.Sheets.Add(after:= _
ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
On Error Resume Next
sh2.Name = uname: If Err.Number <> 0 Then MsgBox "Data already copied": _
sh2.Delete: Exit Sub
On Error GoTo 0
With sh1
.AutoFilterMode = False
Set rng = .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
rng.AutoFilter 1, uname
On Error Resume Next
rng.SpecialCells(xlCellTypeVisible).EntireRow.Copy sh2.Range("A1")
If Err.Number <> 0 Then MsgBox "Data not found" _
Else MsgBox "All matching data has been copied"
.AutoFilterMode = False
On Error GoTo 0
End With
With Application
.ScreenUpdating = True
.DisplayAlerts = True
End With
End Sub