I have a vba macro that is attempting do some light formatting of data in one workbook then paste the formatted data into the bottom of a table in another workbook. For some reason I am getting a Run-time error '1004': PasterSpecial method of Range class failed when it goes to paste and I can't figure out why.
Sub Add_Data()
'
' Add_Data Macro
'
' Insert column to the left of column B in raw data
Columns("B:B").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
' Copy columns A-N in raw data
Range("A2").Select
Selection.End(xlDown).Select
Range("A2:N" & ActiveCell.Row).Select
Selection.Copy
' Remove filter from column B of ongoing report
Windows("Ongoing Report.xlsm").Activate
ActiveSheet.ListObjects("OpenJobs_DATA").Range.AutoFilter Field:=2
' Paste data from raw data at bottom of ongoing report
Range("A2").Select
Selection.End(xlDown).Select
Range("A" & ActiveCell.Row + 1).Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
' Filter column B of ongoing report to remove blanks
ActiveSheet.ListObjects("OpenJobs_DATA").Range.AutoFilter Field:=2, _
Criteria1:="<>"
Copy Range to Range
Carefully adjust the values in the constants (Const) section.
It is assumed that the code will be in the workbook containing the
RawData worksheet.
Tips
The last or first used cells (rows, columns) are usually calculated
from the bottom or from the right. I might be wrong here if you have data below Source Range.
Anything containing Select and Active is best avoided, if possible.
When pasting values, using Copy(Paste) is easily (best) avoided.
The Code
Sub Add_Data()
Const cSource As String = "RawData" ' Source Worksheet Name
Const cCols As String = "A:N" ' Source Columns Range Address
Const cFr As Long = 2 ' Source/Target First Row Number
Const cWbTarget As String = "Ongoing Report.xlsm" ' Target Workbook Name
Const cTarget As String = "Sheet1" ' Target Worksheet Name
Const cTgt As String = "A" ' Target Column Range
Dim rngS As Range ' Source Range
Dim rngT As Range ' Target Range
' In Source Worksheet
With ThisWorkbook.Worksheets(cSource)
' Insert column to the left of column B in raw data.
.Columns("B:B").Insert Shift:=xlToRight, _
CopyOrigin:=xlFormatFromLeftOrAbove
' In Source Columns Range
With .Columns(cCols)
' Calculate and create a reference to Source First Column Last Used
' Cell.
'Set rngS = .Cells(.Row, .Column).End(xlDown)
Set rngS = .Cells(.Rows.Count, .Column).End(xlUp)
' Calculate and create a reference to Source Range.
Set rngS = .Rows(cFr).Resize(rngS.Row - cFr + 1)
End With
End With
' In Target Worksheet
With Workbooks(cWbTarget).Worksheets(cTarget)
' Remove filter from column B of ongoing report
.ListObjects("OpenJobs_DATA").Range.AutoFilter Field:=2
' Calculate and create a reference to Target Column First Empty Cell.
Set rngT = .Cells(.Rows.Count, cTgt).End(xlUp).Offset(1)
' Calculate and create a reference to Target Range i.e. adjust the size
' to be equal to the size of Source Range.
Set rngT = rngT.Resize(rngS.Rows.Count, rngS.Columns.Count)
' Copy values from Source Range to Target Range.
rngT = rngS.Value
' Filter column B of ongoing report to remove blanks
.ListObjects("OpenJobs_DATA").Range.AutoFilter Field:=2, Criteria1:="<>"
End With
End Sub
You lose your clipboard data when you unfilter the table in your destination workbook. They unfiltering first then copying the original range. Also, there almost never a good reason to use select. It can slow down your macros.
Sub Add_Data()
Dim home As Worksheet: Set home = ActiveWorkbook.Sheets("sheet name 1")
Dim dest As Worksheet: Set dest = Windows("Ongoing Report.xlsm").Sheets("sheet name 2")
'Insert column to the left of column B in raw data
home.Columns("B:B").Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
'Remove filter from column B of ongoing report
dest.Sheets("sheet name here").ListObjects("OpenJobs_DATA").Range.AutoFilter Field:=2
'Copy columns A-N in raw data
home.Range("A2", Range("A2").SpecialCells(xlEnd).Copy
'Paste data from raw data at bottom of ongoing report
dest.Range("A" & dest.Range("A2").End(xlDown).Row + 1).PasteSpecial xlPasteValues
'Filter column B of ongoing report to remove blanks
Dest.ListObjects("OpenJobs_DATA").Range.AutoFilter Field:=2, Criteria1:="<>"
End Sub
Untested from my mobile, but hope this points you in the right direction.
Related
I would like the user to be able to click the green button on the right of each named range to insert a new data entry row below the named range header. The code I have hard codes the insert row number for the first named range. I need a way to have the code to be smart enough to know that the first row below the header of the second, third, & forth named range will changed.
Another big part is that the inserted row needs to have the same formatting (dropdowns, formulas, color, etc.) as the rows below.
First named range button code:
Sub BidSheetAddRow_Materials()
' BidSheetAddRow_Materials Macro
Rows("19:19").Select
Selection.Copy
Rows("19:19").Select
Selection.Insert Shift:=xlDown
Range("A19").Select
Application.CutCopyMode = False
Selection.ClearContents
Range("C19").Select
Selection.ClearContents
Range("K19").Select
Selection.ClearContents
End Sub
Based on the screenshot all table headers are in colA, the first input row is 3 cells below the header, and the first input cell on each table row is a merged cell.
So this works for me:
Sub AddMaterial()
AddRow "MATERIALS"
End Sub
Sub AddRate()
AddRow "RATE"
End Sub
Sub AddRow(TableHeader As String)
Dim f As Range, ws As Worksheet, c As Range
Set ws = ThisWorkbook.Worksheets("Input") 'or whatever
Set f = ws.Columns("A").Find(what:=TableHeader, lookat:=xlWhole) 'find the header
If Not f Is Nothing Then
Set c = f.Offset(3) 'step down to first input row below header
Do While c.Offset(1).MergeArea.Cells.Count > 1 'keep looping while `c` is merged
Set c = c.Offset(1)
Loop
c.Offset(1).EntireRow.Insert shift:=xlDown 'insert
c.EntireRow.Copy c.Offset(1) 'copy
c.Offset(1).EntireRow.ClearContents 'clear new row
Else
MsgBox "Table header '" & TableHeader & "' not found!"
End If
End Sub
Before/after:
In advance, I would like to thank anyone who reads this for taking the time to make any suggestions! I have tried other examples I've found on here and none of them seem to work so thanks for any advice!
So essentially I have 3 sheets. In sheet 1, I will be manually entering data into the next empty row (The data spans from Column A to Column U). Sheet 2 is linked to Sheet 1 in a manner to where if I select a row and autofill down to the next one, it will display the data from Sheet 1 (and also increases the values in each cell to account for inflation).
So essentially after I enter data into a new row on Sheet 1, I want to run a macro that will then dynamically autofill the last row on Sheet 2 to the next empty row. I also want this to be repeated going from Sheet 2 to Sheet 3.
An example would be, if Sheet 1 and 2 both have data down to row 35, I want to be able to manually enter data in row 36 and then my macro will autofill row 35 to 36 on Sheet 2.
The code I have written so far is below. To explain, base/basee and home/homee are cells I have named to compare values from specific columns for my if/then statement. I keep getting Error 1004 on the last line where I try and autofill down to the next cell wit Offset(1,0)
Sub PracticeTool()
Dim current1 As Integer
Dim current2 As Integer
Worksheets("City1").Select
Application.Goto Reference:="base"
Selection.End(xlDown).Select
Selection.End(xlDown).Select
current1 = Selection
Worksheets("Inflation").Select
Application.Goto Reference:="basee"
Selection.End(xlDown).Select
Selection.End(xlDown).Select
current2 = Selection
If (current1 <> current2) Then
Application.Goto Reference:="homee"
Selection.End(xlDown).Select
Selection.End(xlDown).Select
Selection.End(xlDown).Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.AutoFill Destination:=Selection.Offset(1, 0), Type:=xlFillDefault
End If
End Sub
Sheet 1 Sample Data: https://i.stack.imgur.com/pTFo5.png
Sheet 2 Sample Data: https://i.stack.imgur.com/kufrV.png
I didnt't get exactly what you wanted to compare, but I think you're close.
This code should solve the requirement.
Read the comments and adjust it to fit your needs.
Public Sub AutoFillSheets()
AutoFillRange "Sheet2", "A", "U"
AutoFillRange "Sheet3", "A", "U"
End Sub
Private Sub AutoFillRange(ByVal targetSheetName As String, ByVal fromColumnLetter As String, toColumnLetter As String)
Dim targetSheet As Worksheet
Dim targetRange As Range
Dim targetLastRow As Long
Set targetSheet = ThisWorkbook.Worksheets(targetSheetName)
' Get the last row in source sheet
targetLastRow = targetSheet.Cells(targetSheet.Rows.Count, 1).End(xlUp).Row
' Set the range to copy
Set targetRange = targetSheet.Range(fromColumnLetter & targetLastRow & ":" & toColumnLetter & targetLastRow)
' You had the error in this line (below). Problem is that to use autofill you need to include the rows from which Excel would calculate the source range (see that I took the last row in the first column, and added one to the second column)
targetRange.AutoFill Destination:=targetSheet.Range(fromColumnLetter & targetLastRow & ":" & toColumnLetter & targetLastRow + 1)
End Sub
I've only just come across VBA so I'm a complete novice. Essentially I'm currently automating a form that requires writing out the questions, potential answers, instructions, etc. for developers on a spreadsheet. I've created a basic template table so all the questions are structured the same. I want to copy and paste this table (clearing the contents and taking off the number of the question) and paste it 2 rows down from the bottom of the last table.
The code works fine if I just wanted to copy and paste the table directly below the first but I can't go any further than that. I'm not sure how to write that I want it to find the last filled in row and paste the table 2 rows below.
Sub Macro2()
'
' Macro2 Macro
'
' Keyboard Shortcut: Ctrl+g
'
Range("C2:G6").Select
Selection.ClearContents
Range("A2").Select
ActiveCell.FormulaR1C1 = "C"
Range("A2:G6").Select
Selection.Copy
Range("A8").Select
ActiveSheet.Paste
End Sub
You can completely avoid using select in order to achieve your goal. In the following code, Source is the range of your table, LastRow finds the last row of your table and DestRng is the destination where you want a copy of your table. Hope this helps!
Sub Macro2()
'
' Macro2 Macro
'
' Keyboard Shortcut: Ctrl+g
'
Dim TblRng As Range
Set TblRng = Range("C2:G6")
TblRng.ClearContents
Dim Source As Range
Set Source = Worksheets("Sheet2").Range(("A2"), Range("G2").End(xlDown))
Source.Copy
Dim LastRow As Long
LastRow = Cells(Rows.Count, "G").End(xlUp).Row
Dim DestRng As Range
Set DestRng = Source(LastRow + 1, "A")
DestRng.PasteSpecial xlPasteAll
End Sub
We are looking to automate this process using Excel VBA/macros because we process two to ten spreadsheets a week.
We want to extract a certain number of rows per a variable set of phone numbers.
For example: a spreadsheet with 200,000 rows has 20,000 rows assigned to ten phone numbers. We want to extract the first ten rows per phone number. Our resulting file will have 100 rows ordered by phone number.
Notes:
We need to extract a variable number of records per phone number.
The number of columns can vary.
The number of rows can vary.
We need the entire row of data.
The phone number column may be in a different place in each spreadsheet.
The number of phone numbers may vary.
Here's a code that works on one file, but cannot be duplicated to another worksheet because the "field", "criteria" and "rows" change per worksheet.
We thought IndexMatch might work, but it only returns one item, rather than duplicates.
We don't have a VBA solution, so we do this manually.
Any help would be appreciated!
Sub ExtractPh()
' Establish filter
' Choose first unique phone number
Cells.Select
Selection.AutoFilter
ActiveSheet.UsedRange.AutoFilter Field:=25, Criteria1:= _
"800-836-9207"
' Copy ten non-sequential rows from row 1 to row 82
Rows("1:82").Select
Selection.Copy
' Add rows to second sheet
Sheets.Add After:=Sheets(Sheets.Count)
Rows("1:1").Select
ActiveSheet.Paste
' Move second to sheet to first position to save as separate file
Sheets("Sheet1").Select
Application.CutCopyMode = False
Sheets("Sheet1").Move Before:=Sheets(1)
' Return to main data sheet
Sheets("Test LKY job").Select
' Choose second unique phone number in column
ActiveSheet.UsedRange.AutoFilter Field:=25, Criteria1:= _
"800-907-3803"
' Choose second set of ten non-sequential rows and paste to first sheet
Rows("6:26").Select
Selection.Copy
Sheets("Sheet1").Select
Rows("12:12").Select
ActiveSheet.Paste
' Return to main data sheet
Sheets("Test LKY job").Select
' Choose third unique phone number in column
ActiveSheet.UsedRange.AutoFilter Field:=25, Criteria1:= _
"800-538-1668"
' Choose third set of non-sequential rows and paste to first sheet
Rows("4:48").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Sheet1").Select
Rows("22:22").Select
ActiveSheet.Paste
End Sub
Here's some sample code showing how you can filter a sheet, grab a specific number of visible rows, then copy those rows to another sheet.
Sub Tester()
Dim rng As Range, rngDest As Range
Set rngDest = Sheet2.Range("A2")
Set rng = GetFirstVisibleRows(ActiveSheet, 1, "A", 10)
If Not rng Is Nothing Then
rng.EntireRow.Copy rngDest
Set rngDest = rngDest.Offset(rng.Cells.Count, 0)
End If
End Sub
'filter the data on a sheet by a given value in a given column, then
' return a range with the first x visible rows
Function GetFirstVisibleRows(sht As Worksheet, filterColumn As Long, _
filterValue, howManyRows As Long) As Range
Dim c As Range, rngVis As Range, rngCopy As Range
'filter the sheet and find the remaining visible rows (if any)
With sht.UsedRange
.AutoFilter
.AutoFilter Field:=filterColumn, Criteria1:=filterValue
On Error Resume Next '<< ignore error if no visible cells
'offset/resize is to ignore the header row...
Set rngVis = .Columns(1).Offset(1, 0).Resize(.Columns(1).Rows.Count - 1).SpecialCells(xlCellTypeVisible)
On Error GoTo 0 '<< stop ignoring errors
End With
If Not rngVis Is Nothing Then
'some visible (not filtered out) rows, so collect the first x of those...
For Each c In rngVis.Cells
If rngCopy Is Nothing Then
Set rngCopy = c
Else
Set rngCopy = Application.Union(c, rngCopy)
End If
'exit loop if we have enough rows
If rngCopy.Cells.Count >= howManyRows Then Exit For
Next c
End If
Set GetFirstVisibleRows = rngCopy
End Function
I am having problems with getting a loop to run.
I have a Source1 spreadsheet with a list of values in Column A on the CC's tab. Each number is to be copied individually into Cell B1 on the Template tab of the Source2 spreadsheet.
Cell B1 triggers a consolidation of information (mainly indexed info) and displays it in a template - an aggregate picture of lots of background data. I then Copy A1:K71, and paste this into the Output tab of the Source1 spreadsheet.
I want to work down the list in Column A of the CC's tab, and append each output from the Source2 spreadsheet into the Output tab automatically.
I have the copy/paste working, but I am having problems with the loop.
Selection.Copy
Windows("Source2.xlsx").Activate
Range("B1").Select
ActiveSheet.Paste
Range("A1:K71").Select
Application.CutCopyMode = False
Selection.Copy
Windows("Source1.xlsm").Activate
Sheets("Ouput").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Selection.PasteSpecial Paste:=xlPasteFormats, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
ActiveWindow.SelectedSheets.HPageBreaks.Add Before:=ActiveCell
#Andrew, after reading and re-reading your question, I don't think a loop of any kind is necessary. The macro-recorder results you gave above provide information about how you can solve this. I tested this by creating a Source1 Workbook with values placed in column A on a sheet labeled CC's. I also added a sheet labeled Output. Then, I opened a second Workbook with a sheet labeled Template. Here is the sub-procedure I used to produce the result I think you are describing above:
Sub AndrewProject()
' COMMENT: Declare variables used throughout this procedure
Dim InitialVals As Range
Dim OutputVals As Range
Dim FinalResults As Range
Dim FinalOutput As Range
Dim cell As Variant
' COMMENT: Set the range objects so they are easier to manipulate
Set InitialVals = Workbooks("Source1").Worksheets("CC's").Range("A2:A72")
Set OutputVals = Workbooks("Source2").Worksheets("Template").Range("B2:B72")
Set FinalResults = Workbooks("Source2").Worksheets("Template").Range("A2:K72")
Set FinalOutput = Workbooks("Source1").Worksheets("Output").Range("A2:K72")
' COMMENT: This line copies the values in Source1 Workbook and pastes them into Source2 Workbook
InitialVals.Copy
OutputVals.PasteSpecial xlPasteValues
' COMMENT: Additional code goes here to create the desired output. To simplify things, I put a
' function in Source2, column K that concatenates the string "Output" with InitialVals copied
' from Source1. To emulate your Source2 Template, I placed random values between 1 and 1000 in
' Cells A2:A72 and C2:J72.
' COMMENT: Copy the FinalResults from Source2 "Template" tab into the Source1 "Output" tab
FinalResults.Copy
FinalOutput.PasteSpecial xlPasteAll
End Sub
OK #Andrew...this has got to be my last attempt. I believe this answers your question.
Sub AutomateIt()
' Declare your variables
Dim cell As Range
Dim Src1CC As Range
Dim Src2Template As Range
Dim Src2Calcs As Range
Dim Src1Output As Range
Dim NextRow As Long
Dim count As Integer
' Set the ranges so they can be manipulated
Set Src1CC = Workbooks("Source1").Worksheets("CC").Range("A1")
Set Src2Template = Workbooks("Source2").Worksheets("Template").Range("B1")
Set Src2Calcs = Workbooks("Source2").Worksheets("Template").Range("A1:K72")
Set Src1Output = Workbooks("Source1").Worksheets("Output").Range("A1:K72")
Src2Template.ClearContents
count = 0
' Loop through all the cells and calculate stuff
For Each cell In Src1CC.Range(Src1CC, Src1CC.End(xlDown))
'Determine the next empty row (plus a space for readability)
NextRow = Cells(Rows.count, 1).End(xlUp).Row + 2
'Send a copy of the Src1CC cell value to the Src2Template
cell.Copy Src2Template
'Re-calculate A1:K72 based on cell value
Src2Calcs.Calculate
'Copy Src2Calcs results and paste to Source1 Output
Src2Calcs.Copy
Src1Output.PasteSpecial xlPasteValues
count = count + 1
MsgBox "You have pasted " & count & " results."
'Change Src1Output Range so that the next paste is the next blank row
'plus one additional row for readability.
Set Src1Output = Workbooks("Source1").Worksheets("Output").Range(Cells(NextRow, 1), Cells(NextRow, 11))
Next cell
End Sub