I import a massive amount of data from SharePoint as a new worksheet (“The Pull”) on an existing book that has four other sections. I am trying to develop a macro that will, when run, a.) automatically filter the data by a field in the Pull; b.) copy/”paste values” of that filtered data into an existing sheet starting at cell A5; c.) reset the filter on the Pull for the next sheet.
So for example, in the Pull (default worksheet name “owssvr”), each row has a date in Column AR showing when the item in that row was created. How do I automatically filter for all items from the previous month in the pull (or alternatively, give the user the option to choose the month), and copy/paste values of the filtered result into a worksheet called “Monthly Report” starting at cell A5 (allowing for the header to not change)? Is this possible?
This is how I would write that:
Option Explicit
Sub MonthFilter()
Dim LR As Long, MyDate As Date, d1 As Date, d2 As Date
MyDate = Application.InputBox("Enter any date in the month you wish to pull", "Enter Date", Date - 30, Type:=2)
If MyDate = 0 Then
Exit Sub
Else
d1 = DateSerial(Year(MyDate), Month(MyDate), 1)
d2 = DateSerial(Year(MyDate), Month(MyDate) + 1, 1) - 1
End If
With Sheets("The Pull")
.AutoFilterMode = False
.Rows(1).AutoFilter
.Rows(1).AutoFilter 44, Criteria1:=">=" & d1, _
Operator:=xlAnd, Criteria2:="<=" & d2
LR = .Cells(.Rows.Count, 44).End(xlUp).Row
If LR > 1 Then .Range("A2:A" & LR).EntireRow.Copy Sheets("Monthly Report").Range("A5")
.AutoFilterMode = False
End With
End Sub
You can use AutoFilter and ShowAllData to filter and unfilter. Here is an example.
Sub CopyLastMonthFromThePull(shtCopyTo As Worksheet)
Dim rngPullTable As Range, iColumnToFilter As Integer, strMonth As String
' this assumes that the pull data is the first Excel Table on ThePull worksheet named owssvr
Set rngPullTable = ThisWorkbook.Worksheets("owssvr").ListObjects(1).Range
rngPullTable.Parent.Activate
' determine the filter details
strMonth = CStr(DateSerial(Year(Date), Month(Date) - 1, Day(Date))) ' one month prior to today
iColumnToFilter = 44 ' Column AR is the 44th column
' filter the table
rngPullTable.AutoFilter Field:=iColumnToFilter, Operator:=xlFilterValues _
, Criteria2:=Array(1, strMonth)
DoEvents
' copy the filtered results. (This also copies the header row.)
rngPullTable.Copy
With shtCopyTo
.Activate
.Range("A5").PasteSpecial xlPasteFormulasAndNumberFormats
.Columns.AutoFit
.Range("A1").Select
End With
Application.CutCopyMode = False
' remove filter
With rngPullTable.Parent
.Activate
.ShowAllData
End With
rngPullTable.Range("A1").Select
' End with the sheet being copied to active
shtCopyTo.Activate
End Sub
Related
I have a an inventory spreadsheet consisting of a transaction table detailing in each column the date a piece of inventory is "received" into stock or "sold", description, quantity, and whether it was "received" or "sold." All of this information is fed into the table at the click of a button through a macro I have created. Is there a way using VBA that if the value of any piece of inventory falls below zero (due to trying to sell inventory not in stock) a Msgbox could be displayed and the latest line of information fed into the transaction table could be deleted?
Any help is greatly appreciated.
Thank you.
EDIT:
Here is the code that I'm using to populate the data from cells A4:E4 into the table called "Table1."
Sub PlaceOrder()
Dim tbl As ListObject
Dim LastRow As Long
Set tbl = ActiveSheet.ListObjects("Table1")
LastRow = tbl.Range.Rows.Count 'get # of last row
With ActiveSheet
'copy and paste A4
.Range("A4").Copy
tbl.Range(LastRow, 1).Offset(1, 0).PasteSpecial Paste:=xlPasteValuesAndNumberFormats
'copy and paste B4
.Range("B4").Copy
tbl.Range(LastRow, 2).Offset(1).PasteSpecial Paste:=xlPasteFormulas
'copy and paste C4:F4
.Range("C4:F4").Copy
tbl.Range(LastRow, 3).Offset(1).PasteSpecial Paste:=xlPasteValuesAndNumberFormats
'clear value in B4:E4
.Range("B4:E4").ClearContents
End With
End Sub
EDIT 2: Here is a snapshot of an old stock transaction tracker that I am using as a reference in order to create this inventory spreadsheet. It will be using a lot of the same functionality. So.... I'd like to use the value in the table column "Qty" that corresponds with the value in the table column "Stock (Exchange:Ticker)" in order to determine if an item falls below zero in quantity.
example
Based on your comments, try this version:
EDIT:
Sub PlaceOrder()
With ActiveSheet
Dim tbl As ListObject
Set tbl = .ListObjects("Table1")
Dim LastRow As Long
LastRow = tbl.Range.Rows.Count 'get # of last row
'====== Copy all content `
'copy and paste A4
.Range("A4").Copy
tbl.Range(LastRow, 1).Offset(1, 0).PasteSpecial Paste:=xlPasteValuesAndNumberFormats
'copy and paste B4
.Range("B4").Copy
tbl.Range(LastRow, 2).Offset(1).PasteSpecial Paste:=xlPasteFormulas
'copy and paste C4:F4
.Range("C4:F4").Copy
tbl.Range(LastRow, 3).Offset(1).PasteSpecial Paste:=xlPasteValuesAndNumberFormats
'clear value in B4:E4
.Range("B4:E4").ClearContents
End With
Dim stockList As Variant
Dim qtyList As Variant
stockList = tbl.ListColumns("Stock (Exchange:Ticker)").DataBodyRange.Value
qtyList = tbl.ListColumns("Qty").DataBodyRange.Value
'=== Start checking inventory of all stocks ===
Dim inventoryDict As Object
Set inventoryDict = CreateObject("Scripting.Dictionary")
Dim i As Long
'Get a unique list of stocks
For i = 1 To UBound(stockList, 1)
If inventoryDict.Exists(stockList(i, 1)) Then
inventoryDict(stockList(i, 1)) = inventoryDict(stockList(i, 1)) + qtyList(i, 1)
Else
inventoryDict.Add stockList(i, 1), qtyList(i, 1)
End If
Next i
'Loop through each stock and check if it's < 0
Dim dictItem As Variant
For Each dictItem In inventoryDict
If inventoryDict(dictItem) < 0 Then
'Inventory is negative
MsgBox "Display Message that inventory is not in stock" & vbNewLine & vbNewLine & _
dictItem & ": " & inventoryDict(dictItem)
tbl.ListRows(tbl.ListRows.Count).Delete
Exit For
End If
Next dictItem
End Sub
This will do the following:
Enter the data in A4:F4 in a new row (unchanged)
Get a list of unique stock in column "Stock (Exchange:Ticker)" and sum up qty of the same stock.
Loop through each stock and check if it's < 0, display Msgbox and delete the last row (which is the data that was just entered).
Rather simple question that I seem to be struggling with.
I have 2 columns. column1 has dates in it where each cell is the day after the previous. column 2 occasionally has data in it. i need to apply an auto filter show all dates except the 15th and 26th of the month for column 1 and all blank cells in column2. I can do the column2 filter with the code
ws1.Range(Cells(4, 1), Cells(x, 2)).AutoFilter field:=2, Criteria1:="="
but i cant figure out how to filter column 1.
any help would be appreciated.
Thank you.
i need to apply an auto filter show all dates except the 15th and 26th of the month for column 1
You can achieve what you want using an array of relevant dates.
Logic: Create an array and then use Criteria2 paramenter to filter. For example if you record a macro you will get something like this Criteria2:=Array(2, "1/1/2020", 2, "1/4/2020", 2, "1/6/2020").... First dimension of array is the time period group. 2 is for Days.
Is this what you are trying?
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim rng As Range
Dim lRow As Long, i As Long
Dim DateArray As Variant
Dim tmpDateString As String: tmpDateString = "2"
'~~> Set your worksheet here
Set ws = Sheet2
With ws
'~~> Remove any autofilter
.AutoFilterMode = False
'~~> Find the last row
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Check if there is any data
If lRow > 1 Then
'~~> Construct your range
Set rng = .Range("A1:A" & lRow)
'~~> Create a string with relevant dates
For i = 2 To lRow
Select Case Day(.Range("A" & i).Value)
Case 15, 16
Case Else
tmpDateString = tmpDateString & "," & .Range("A" & i).Value & ",2"
End Select
Next i
End If
'~~> Split and store in an array
tmpDateString = Mid(tmpDateString, 1, Len(tmpDateString) - 2)
DateArray = Split(tmpDateString, ",")
'~~> Autofilter using an array
rng.AutoFilter Field:=1, Criteria2:=DateArray, Operator:=xlFilterValues
End With
End Sub
In Action
If i could get it to show only 15th and 26th that would work as well as im filtering to delete unused dates. – matthew wilcox 1 hour ago
Swap the Case code in Select Case. Give it a try ;)
I have a spreadsheet with Student Name, Race/Ethnicity, Gender, Degree, Major, Status, Year Started, and Career After Graduation as columns. Thank you for those who helped me with my codes for requiring input in columns Race/Ethnicity, Gender, and Degree if Student Name is provided in column A. Now I need to do something additional. If the value of Status in column F is "Graduated", I want Career After Graduation column (column H) to be filled out too. The closest codes I could come up with are listed below, and I now have a problem.
When Status in column F has a value of "Graduated", Excel not only requires a user to fill out Career After Graduation in column H, but also other columns. How should I modify the codes, so only column H will be required?
Thank you!
Option Explicit
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Dim rngCell As Range, strBlanks As String
Application.ScreenUpdating = False
strBlanks = vbNullString
For Each rngCell In Worksheets("Sheet1").Range("F2:F20").Cells
If rngCell.Value = "Graduated" Then
If WorksheetFunction.CountA(rngCell.Offset(0, 2).Resize(1, 1)) < 1 Then
strBlanks = strBlanks & IIf(Len(strBlanks) > 0, ",", "") & _
Replace(rngCell.Offset(0, 2).Resize(1,1).SpecialCells(xlCellTypeBlanks).Address, "$", "")
End If
End If
Next
If Not strBlanks = vbNullString Then
MsgBox "Entries required in cells " & vbCrLf & vbCrLf & strBlanks
Cancel = True
Exit Sub
End If
End Sub
you could use Autofilter to avoid looping through cells and have a one-shot operation
Option Explicit
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Dim toBeFilledAddress As String
With Worksheets("Sheet1") '<--| '<-- change "Sheet1" with your actual sheet name
With .Range("A1:H" & .Cells(.Rows.Count, 1).End(xlUp).Row) '<--| reference its range in columns A:B from row 1 to column "A" last non empty cell row
.AutoFilter field:=6, Criteria1:="Graduated" '<--| filter referenced range on its 6th column with "Graduated"
.AutoFilter field:=8, Criteria1:="" '<--|filter referenced range again on its 8th column with blanks
If Application.WorksheetFunction.Subtotal(103, .Resize(, 1)) > 1 Then toBeFilledAddress = .Offset(1, 7).Resize(.Rows.Count - 1, 1).SpecialCells(xlCellTypeVisible).Address(False, False) '<--| store all matching cells address
End With
.AutoFilterMode = False '<--| show all rows back
End With
If toBeFilledAddress <> "" Then '<--| if any cell other than header ones has been filtered...
MsgBox "Entries required in cells " & vbCrLf & vbCrLf & toBeFilledAddress
Cancel = True
Exit Sub '<--| this line could be avoided unless you're planning to add more lines after "End If"
End If
End Sub
why do you use the following?
& vbCrLf & vbCrLf
it seems that your code works fine but some how those two variables are getting filled with other columns.
Also, which of the columns does it return besides H? And when is the user supposed to be prompted to enter in the values?
I am trying to create a single VBA that searches seven different sheets for a particular entry in Column E and then copy the entire row into a 8th Sheet and placing them in order by column A.
I got the point for it to search for one spreadsheet and copying the items over to the other in the exact same row they are located on the spreadsheet
Sub Test()
Dim rw As Long, Cell As Range
For Each Cell In Tues.Range("E:E")
rw = Cell.Row
If Cell.Value = "No" Then
Cell.EntireRow.Copy
Sheets("Completed").Range("A" & rw).PasteSpecial
End If
Next
End Sub
The Spreadsheets I want to search for are:
Mon
Tues
Wed
Thurs
Fri
Sat
Sun
The sheet I want to move it to is called Completed, then I want it to sort by Column A.
Any Ideas?
How about this:
Sub loop_through_WS()
Dim rw As Long, i As Long, lastRow As Long, compLastRow&
Dim cel As Range
Dim mainWS As Worksheet, ws As Worksheet
Dim sheetArray() As Variant
sheetArray() = Array("Mon", "Tues", "Weds", "Thurs", "Fri", "Sat", "Sun")
Set mainWS = Sheets("Completed")
compLastRow = mainWS.Cells(mainWS.Rows.Count, 1).End(xlUp).row
For i = LBound(sheetArray) To UBound(sheetArray)
With Sheets(sheetArray(i))
lastRow = .Cells(.Rows.Count, 5).End(xlUp).row
For Each cel In .Range("E1:E" & lastRow)
rw = cel.row
If cel.Value = "No" Then
cel.EntireRow.copy
mainWS.Range("A" & compLastRow).pasteSpecial
compLastRow = mainWS.Cells(mainWS.Rows.Count, 1).End(xlUp).row + 1
End If
Next
End With
Next i
Application.CutCopyMode = False
End Sub
It basically uses the code you gave, but I added the worksheet loop (it'll loop through each of the day worksheets) and paste back onto the "Completed" WS.
See if you can work out how I looped through the worksheets - I use this type of thing often so it'd be good to learn if you are doing much of this. It also allows you to add another sheet (say "Weekend") to your workbook and all you have to do is add "Weekend" after "Sun" in the Array. That's the only place you'll need to add it.
One note is that I changed your for each Cell in Range(E:E) to be from E1 to the last Row in column E - which makes the macro run way faster.
Edit: As mentioned in my comment above, it's generally not recommended to use Cell as a variable name. (Same goes for Column, Row, Range, etc.) because these all mean something specifically to VBA (i.e. Cell([row],[column]). Instead, as you see, I like to use cel or rng or iCell,etc.
Something like this should work for you based on what you've described. It uses a For Each loop to iterate through the sheets and uses the AutoFilter method to find what it's looking for from column E. The code assumes headers are in row 1 on each sheet. I attempted to comment it for clarity.
Sub tgr()
Dim wb As Workbook
Dim ws As Worksheet
Dim wsCompleted As Worksheet
Dim bHeaders As Boolean
Set wb = ActiveWorkbook
Set wsCompleted = wb.Sheets("Completed")
bHeaders = False
'Comment out or delete the following line if you do not want to clear current contents of the Completed sheet
wsCompleted.Range("A2", wsCompleted.Cells(Rows.Count, Columns.Count)).Clear
'Begin loop through your sheets
For Each ws In wb.Sheets
'Only perform operation if sheet is a day of the week
If InStr(1, " Mon Tue Wed Thu Fri Sat Sun ", " " & Left(ws.Name, 3) & " ", vbTextCompare) > 0 Then
'If headers haven't been brought in to wsCompleted yet, copy over headers
If bHeaders = False Then
ws.Rows(1).EntireRow.Copy wsCompleted.Range("A1")
bHeaders = True
End If
'Filter on column E for the word "No" and copy over all rows
With ws.Range("E1", ws.Cells(ws.Rows.Count, "E").End(xlUp))
.AutoFilter 1, "no"
.Offset(1).Resize(.Rows.Count - 1).EntireRow.Copy wsCompleted.Cells(wsCompleted.Rows.Count, "A").End(xlUp).Offset(1)
.AutoFilter
End With
End If
Next ws
'Sort wsCompleted by column A
wsCompleted.Range("A1").CurrentRegion.Sort wsCompleted.Range("A1"), xlAscending, Header:=xlGuess
End Sub
EDIT: Here is the sample workbook that contains the code. When I run the code, it works as intended. Is your workbook data setup drastically different?
https://drive.google.com/file/d/0Bz-nM5djZBWYaFV3WnprRC1GMnM/view?usp=sharing
The answers posted earlier have some great stuff in them, but I think this will get you exactly what you after with no issues and also with great speed. I made some assumptions on how your data is laid out, but commented them. Let me know how it goes.
Sub PasteNos()
Dim wsComp As Worksheet
Dim vSheets() As Variant
Application.ScreenUpdating = False
vSheets() = Array("Mon", "Tues", "Weds", "Thurs", "Fri", "Sat", "Sun")
Set wsComp = Sheets("Completed")
For i = LBound(vSheets) To UBound(vSheets)
With Sheets(vSheets(i))
.AutoFilterMode = False
.Range(.Range("E1"), .Cells(.Rows.Count, 5).End(xlUp)).AutoFiler 1, "No"
'assumes row 1 has headers
.Range(.Range("E2"), .Cells(.Rows.Count, 5).End(xlUp)).SpecialCells(xlCellTypeVisible).EntireRow.Copy
'pastes into next available row
With wsComp
.Range("A" & .Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues 'assumes copy values over
End With
End With
Next i
'assumes ascending order, headers in row 1, and that data is row-by-row with no blank rows
wsComp.UsedRange.Sort 1, xlAscending, Header:=xlYes
Application.ScreenUpdating = True
End Sub
Is there a macro or a way to conditionally copy rows from one worksheet to another in Excel 2003?
I'm pulling a list of data from SharePoint via a web query into a blank worksheet in Excel, and then I want to copy the rows for a particular month to a particular worksheet (for example, all July data from a SharePoint worksheet to the Jul worksheet, all June data from a SharePoint worksheet to Jun worksheet, etc.).
Sample data
Date - Project - ID - Engineer
8/2/08 - XYZ - T0908-5555 - JS
9/4/08 - ABC - T0908-6666 - DF
9/5/08 - ZZZ - T0908-7777 - TS
It's not a one-off exercise. I'm trying to put together a dashboard that my boss can pull the latest data from SharePoint and see the monthly results, so it needs to be able to do it all the time and organize it cleanly.
This works: The way it's set up I called it from the immediate pane, but you can easily create a sub() that will call MoveData once for each month, then just invoke the sub.
You may want to add logic to sort your monthly data after it's all been copied
Public Sub MoveData(MonthNumber As Integer, SheetName As String)
Dim sharePoint As Worksheet
Dim Month As Worksheet
Dim spRange As Range
Dim cell As Range
Set sharePoint = Sheets("Sharepoint")
Set Month = Sheets(SheetName)
Set spRange = sharePoint.Range("A2")
Set spRange = sharePoint.Range("A2:" & spRange.End(xlDown).Address)
For Each cell In spRange
If Format(cell.Value, "MM") = MonthNumber Then
copyRowTo sharePoint.Range(cell.Row & ":" & cell.Row), Month
End If
Next cell
End Sub
Sub copyRowTo(rng As Range, ws As Worksheet)
Dim newRange As Range
Set newRange = ws.Range("A1")
If newRange.Offset(1).Value <> "" Then
Set newRange = newRange.End(xlDown).Offset(1)
Else
Set newRange = newRange.Offset(1)
End If
rng.Copy
newRange.PasteSpecial (xlPasteAll)
End Sub
Here's another solution that uses some of VBA's built in date functions and stores all the date data in an array for comparison, which may give better performance if you get a lot of data:
Public Sub MoveData(MonthNum As Integer, FromSheet As Worksheet, ToSheet As Worksheet)
Const DateCol = "A" 'column where dates are store
Const DestCol = "A" 'destination column where dates are stored. We use this column to find the last populated row in ToSheet
Const FirstRow = 2 'first row where date data is stored
'Copy range of values to Dates array
Dates = FromSheet.Range(DateCol & CStr(FirstRow) & ":" & DateCol & CStr(FromSheet.Range(DateCol & CStr(FromSheet.Rows.Count)).End(xlUp).Row)).Value
Dim i As Integer
For i = LBound(Dates) To UBound(Dates)
If IsDate(Dates(i, 1)) Then
If Month(CDate(Dates(i, 1))) = MonthNum Then
Dim CurrRow As Long
'get the current row number in the worksheet
CurrRow = FirstRow + i - 1
Dim DestRow As Long
'get the destination row
DestRow = ToSheet.Range(DestCol & CStr(ToSheet.Rows.Count)).End(xlUp).Row + 1
'copy row CurrRow in FromSheet to row DestRow in ToSheet
FromSheet.Range(CStr(CurrRow) & ":" & CStr(CurrRow)).Copy ToSheet.Range(DestCol & CStr(DestRow))
End If
End If
Next i
End Sub
The way I would do this manually is:
Use Data - AutoFilter
Apply a custom filter based on a date range
Copy the filtered data to the relevant month sheet
Repeat for every month
Listed below is code to do this process via VBA.
It has the advantage of handling monthly sections of data rather than individual rows. Which can result in quicker processing for larger sets of data.
Sub SeperateData()
Dim vMonthText As Variant
Dim ExcelLastCell As Range
Dim intMonth As Integer
vMonthText = Array("January", "February", "March", "April", "May", _
"June", "July", "August", "September", "October", "November", "December")
ThisWorkbook.Worksheets("Sharepoint").Select
Range("A1").Select
RowCount = ThisWorkbook.Worksheets("Sharepoint").UsedRange.Rows.Count
'Forces excel to determine the last cell, Usually only done on save
Set ExcelLastCell = ThisWorkbook.Worksheets("Sharepoint"). _
Cells.SpecialCells(xlLastCell)
'Determines the last cell with data in it
Selection.EntireColumn.Insert
Range("A1").FormulaR1C1 = "Month No."
Range("A2").FormulaR1C1 = "=MONTH(RC[1])"
Range("A2").Select
Selection.Copy
Range("A3:A" & ExcelLastCell.Row).Select
ActiveSheet.Paste
Application.CutCopyMode = False
Calculate
'Insert a helper column to determine the month number for the date
For intMonth = 1 To 12
Range("A1").CurrentRegion.Select
Selection.AutoFilter Field:=1, Criteria1:="" & intMonth
Selection.Copy
ThisWorkbook.Worksheets("" & vMonthText(intMonth - 1)).Select
Range("A1").Select
ActiveSheet.Paste
Columns("A:A").Delete Shift:=xlToLeft
Cells.Select
Cells.EntireColumn.AutoFit
Range("A1").Select
ThisWorkbook.Worksheets("Sharepoint").Select
Range("A1").Select
Application.CutCopyMode = False
Next intMonth
'Filter the data to a particular month
'Convert the month number to text
'Copy the filtered data to the month sheet
'Delete the helper column
'Repeat for each month
Selection.AutoFilter
Columns("A:A").Delete Shift:=xlToLeft
'Get rid of the auto-filter and delete the helper column
End Sub
This is partially pseudocode, but you will want something like:
rows = ActiveSheet.UsedRange.Rows
n = 0
while n <= rows
if ActiveSheet.Rows(n).Cells(DateColumnOrdinal).Value > '8/1/08' AND < '8/30/08' then
ActiveSheet.Rows(n).CopyTo(DestinationSheet)
endif
n = n + 1
wend
If this is just a one-off exercise, as an easier alternative, you could apply filters to your source data, and then copy and paste the filtered rows into your new worksheet?