I have a Workbook in which there is a sheet named "tracker" that shows certain actionables that need to be closed by team member by target date. I can do it on excel using filters. But I tried ti build a VBA code to automate the process which is
Search for Status of action in column 28. If it is "Open" then Check if "target date" in column 43 is exited as of today. I put today date in column 46. If Target date is exceeded then I want that row to be copy pasted in another worksheet "Open Items". The code should move to next item in 2 situations, either the status is "closed" of Target date is yet to arrive.
Following is code I wrote. The code is executed properly but I get only the last row as output in Open items sheet. The code do not seem to check for status or dates properly
Sub OpenItems()
Application.ScreenUpdating = False
Dim i As Long
Dim Lastrow As Long
Sheets("Open Items").Select
Cells.Select
'Range("E16").Activate
Selection.Delete Shift:=xlUp
Sheets("Observation Tracker").Select
Range("A2").Select
Sheets("Observation Tracker").Activate
Lastrow = Cells(Rows.Count, "AQ").End(xlUp).Row
Dim Lastrowa As Long
Lastrowa = Sheets("Observation Tracker").Cells(Rows.Count,"AU").End(xlUp).Row + 1
For i = 2 To Lastrow
If Cells(i, 28).Value = "Open" Then
If Cells(i, 43).Value < Cells(i, 46).Value Then
Rows(i).Copy Sheets("Open items").Rows(Lastrow)
i = i + 1
End If
End If
Next
Sheets("Observation Tracker").Select
Rows("1:1").Select
Selection.Copy
Sheets("Open Items").Select
Range("AI1").Select
Selection.End(xlToLeft).Select
ActiveSheet.Paste
Range("A1").Select
MsgBox "Open Items Extracted"
Application.ScreenUpdating = True
End Sub
I want all open items with dates passed by to populate in the Open Item worksheet
This line here Rows(i).Copy Sheets("Open items").Rows(Lastrow) will always paste to the same row because you never increment lastrow. So as your code loops through the sheet the output is constantly being overwritten until the last match is made which is the only one you will see.
Rows(i).Copy Sheets("Open items").Rows(Lastrow)
lastrow = lastrow + 1
I don't think you need i = i + 1 either because your for loop is already incrementing i so you are skipping a line every time it gets there.
EDIT:
Here is what I came up with.
Sub OpenItems()
Dim i As Long
Dim lastrow As Long
Dim lastcol As Long
Dim pasteiter As Long
Application.ScreenUpdating = False
With Sheets("Open Items")
'This will clear the contents of Open Items
lastrow = .Cells(Rows.Count, 43).End(xlUp).Row
lastcol = .Cells.Find(What:="*", after:=ActiveSheet.Cells(1, 1), LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False).Column
.Range(.Cells(2, 1), .Cells(lastrow, lastcol)).ClearContents
End With
pasteiter = 2 'Make sure we don't overwrite anything
With Sheets("Observation Tracker")
lastrow = .Cells(Rows.Count, "AQ").End(xlUp).Row
For i = 2 To lastrow
'Combined the two IF statements since we weren't using the outer else.
If (.Cells(i, 28).Value = "Open") And (.Cells(i, 43).Value <= .Cells(i, 46).Value) Then
.Rows(i).Copy Sheets("Open Items").Rows(pasteiter)
pasteiter = pasteiter + 1
End If
Next
.Rows(1).Copy Sheets("Open Items").Rows(1) 'Grab the headers
End With
Application.ScreenUpdating = True
MsgBox "Open Items Extracted"
'I'm not sure what your last bits of code did I removed them.
End Sub
If open items sheet is blank just put something in the first row the first time you run this otherwise you will get a with/object error. Should only occur the first time though.
I removed all your selections and activates, they aren't necessary, slow things down, and obfuscate your code. I also removed lastrowa as it didn't appear to be used.
Related
I have a constant task at work where I need to copy a list of numbers to another sheet. In that sheet, I need to paste those numbers one by one, in a cell to the right of cells with a certain value(that repeats in a column). (notice that the target table is sorted by that value -"מודל תגובה" and there are hidden rows.
It's hard to explain so I hope the images will do.
I tried to write suitable code but I kept getting different errors.
It seems that problems occur when copying the cell values to the target cells.
Dim i As Integer
i = 4
Do While IsEmpty(Cells(i, 1).Value) = False
Worksheets(1).Select
Cells(i, 1).Copy
Worksheets(2).Select
Cells.Find(What:="מודל תגובה", After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
ActiveCell.Offset(0, -1).Activate
If IsEmpty(ActiveCell.Value) = False Then
Selection.FindNext(After:=ActiveCell).Activate
ActiveCell.Offset(0, -1).Paste
Else
ActiveCell.Offset(0, -1).Select
ActiveCell.Paste
End If
i = i + 1
Loop
sorry for the shitty code(literally my first macro).
The solution would be to loop through the visible cells of the filtered range only.
Make sure the destination is filtered for "מודל תגובה" before running this code. It needs to look like your second image before running this code.
Dim SourceSheet As Worksheet
Set SourceSheet = Worksheets(1)
Dim DestinationSheet As Worksheet
Set DestinationSheet = Worksheets(2)
Dim LastRow As Long
LastRow = DestinationSheet.Cells(DestinationSheet.Rows.Count, "B").End(xlUp).Row
Dim VisibleCells As Range
On Error Resume Next 'next line errors if no visible cells so we turn error reporting off
Set VisibleCells = DestinationSheet.Range("A2", "A" & LastRow).SpecialCells(xlCellTypeVisible)
On Error Goto 0 'turn error reporting on or you won't see if other errors occur
If VisibleCells Is Nothing Then 'abort if no cells are visible in the filter
MsgBox "No cells to paste at"
Exit Sub
End If
Dim SourceRow As Long
SourceRow = 4 'start row in your source sheet
Dim Cell As Range
For Each Cell In VisibleCells.Cells 'loop through visible cells
Cell.Value = SourceSheet.Cells(SourceRow, "A").Value 'copy value
SourceRow = SourceRow + 1 'incerease source row
Next Cell
Make sure to define DestinationSheet and SourceSheet with your sheets names.
Try this:
Dim i As Integer
Dim Last_Row as Long
Worksheets(1).Select
'The "1" Of the line below means that the variable gonna count the rows of the first column (A)
Last_Row = Application.ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
Range("A1:A" & Last_Row).Copy
Worksheets(2).Select
Range("A1").Select
ActiveSheet.Paste
As suggested yesterday, I'm splitting my question into two parts, although I think they might be connected:
I have an Excel-macro that basically works, but it gets slower the more sheets are added by the macro. when I create only a few sheets (~100) it's ok but sometime it creates up to a few hundred sheets and every sheet is a different report, and I have to keep all the sheets. An then the issue starts.
Afer the macro creates all those sheets, I do tings like sorting an printing. But before the macro continues with this tasks it takes a very long time, depending on how many sheets I've just produced.
A few years ago I had an issue with a slow macro. Then I found the hint with a forced pause. I've tried it with this macro again, and it improved the speed by a huuuge amount of time. How come a pause speeds up a macro?
The code is a bit longer so I've reduced it to address the issue and marked it in the code:
Sub My_Issues()
Dim ColumnLetter As String, item As String
Dim cell As Range
Dim sheetCount As Integer, TotalRow As Integer, TotalCol As Integer
Dim uniqueArray As Variant
Dim lastRow As Long, x As Long
Application.ScreenUpdating = False
'Get unique brands:
With Sheets("Brand")
.Columns(1).EntireColumn.Delete
Sheets("Sales").Columns("R:R").AdvancedFilter Action:=xlFilterCopy, CopyToRange:=.Range("A1"), Unique:=True
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
If .Range("A3:A" & lastRow).Cells.Count = 1 Then
ReDim uniqueArray(1, 1)
uniqueArray(1, 1) = .Range("A3")
Else
uniqueArray = .Range("A3:A" & lastRow).Value
End If
End With
TotalRow = Sheets("Sales").UsedRange.Rows.Count
TotalCol = Sheets("Sales").UsedRange.Columns.Count
ColumnLetter = Split(Cells(1, TotalCol).Address, "$")(1) 'Num2Char
sheetCount = 0 'Counter for statusbar
For x = 1 To UBound(uniqueArray, 1)
item = uniqueArray(x, 1) 'item=Brand
'Filter sales for each brand:
With Sheets("Sales")
.Range(.Cells(2, 1), .Cells(TotalRow, TotalCol)).AutoFilter Field:=18, Criteria1:=item
End With
With Sheets("Agents")
'Delete old...
.Range(.Cells(2, 1), .Cells(2, 1).End(xlDown)).Clear
'...and get new
Sheets("Sales").Range(Sheets("Sales").Cells(3, 2), Sheets("Sales").Cells(2, 2).End(xlDown)).SpecialCells(xlCellTypeVisible).Copy
.Range("A2").PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
End With
'List with all agents
For Each cell In Worksheets("Agents").Range("A2", Worksheets("Agents").Range("A1").End(xlDown))
With Sheets("Report")
.Range("I4") = cell 'Copy agent and update the formulas within the report
.Range(.PageSetup.PrintArea).Copy
Sheets.Add After:=Sheets("Report")
Selection.PasteSpecial Paste:=xlPasteAllUsingSourceTheme, Operation:=xlNone _
, SkipBlanks:=False, Transpose:=False
Selection.PasteSpecial Paste:=xlPasteColumnWidths, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
ActiveSheet.UsedRange.Value = ActiveSheet.UsedRange.Value 'Replace all formulas with values
Application.CutCopyMode = False
ActiveSheet.Name = cell
sheetCount = sheetCount + 1
If sheetAnz Mod 10 = 0 Then Application.StatusBar = sheetAnz 'Get statusupdate every 10 sheets
End With
Next
'->Issue: I create up to 400 sheets and when I want to continue and do some sorting of the sheets for example it takes a very long time.
'But if I add this break for a second, it works reasonably fine again. Why is that? Does vba needs the break to catch up with itself?
'Since the issue is not the sorting and the other stuff after the pause.
Application.Wait (Now + TimeValue("0:00:01"))
'Continue with other stuff.... sorting sheets and so on
Next
Application.ScreenUpdating = True
End Sub
Any ideas on this issue?
Everyone I am new to code and VBA Excell.
I have a Sub that works, I'm just not sure if it's the right way to do it or if there is a more efficient way as it takes a while to complete when run.
I was just wondering if someone can have a look and maybe give me some pointers.
I will put my code below I hope I'm doing this right.
Thanks
Carly
Sub DataPopulate()
Dim wb As Workbook
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim rng1 As Range
Dim num As Range
Set wb = ActiveWorkbook
Set ws1 = Worksheets("Sheet1")
Set ws2 = Worksheets("Sheet2")
Set rng1 = Range("F2")
Set num = ws1.Range("F2:F4")
'When you click the Click this to populate data MSRP Pricing button you will get the yes no message box.
If MsgBox("Click yes to continue" & vbCrLf & "Excel may say not responding!!!" _
& vbCrLf & "It just may take a few moments", vbYesNo + vbQuestion) = vbYes Then
'If the yes button is pushed in the message box.
ws1.Activate
Range("e18") = ("MSRP List")
'MSRP List text is copied to cell e18.
Range("h2:h16").Value = Range("g2:g16").Value
'The product group list is copied from colum g to h.
ActiveWorkbook.Worksheets("Sheet1").AutoFilter.Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").AutoFilter.Sort.SortFields.Add2 Key:= _
Range("f2:f16"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("Sheet1").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
'The numbers in f2~f16 is sorted in assending order along with the product group name.
End With
Dim Lastrow As Integer
Lastrow = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
ws1.Activate
Range("A23:L" & Lastrow).ClearContents ' Select
'Selection.ClearContents
'Count from A23 to column L and the last row with data, then select that and delete.
Range("A22") = ("Group")
Range("b22") = ("Description")
Range("c22") = ("Code")
Range("d22") = ("Barcode")
Range("e22") = ("List Number")
'Copy the data list headings
a = ws2.Cells(Rows.Count, 1).End(xlUp).Row
'Count rows of CSV data on sheet2 and set veriable for "a" this is the number of times to run the loop below.
'MsgBox (a) '<testing count number
For i = 2 To a
Dim d As Range
If ws1.Range("f2").Value = ("1") And ws2.Cells(i, 1).Value = ws1.Range("g2") Then
'Checking if order of product group f2 = 1
'and if there is a match in sheet2 column A row 1 with G2 in product group list
b = ws1.Cells(Rows.Count, 1).End(xlUp).Row
ws2.Rows(i).Copy
ws1.Cells(b + 1, 1).PasteSpecial Paste:=xlPasteValues
'Then copy that row to sheet1 in the next empty row
End If
'Loop will do the next rows till "a" times loops are done
Next
'This is the same for below until all product groups are done
For i = 2 To a
If ws1.Range("f3") = 2 And ws2.Cells(i, 1).Value = ws1.Range("g3") Then
b = ws1.Cells(Rows.Count, 1).End(xlUp).Row
ws2.Rows(i).Copy
ws1.Cells(b + 1, 1).PasteSpecial Paste:=xlPasteValues
End If
Next
For i = 2 To a
If ws1.Range("f4") = 3 And ws2.Cells(i, 1).Value = ws1.Range("g4") Then
b = ws1.Cells(Rows.Count, 1).End(xlUp).Row
ws2.Rows(i).Copy
ws1.Cells(b + 1, 1).PasteSpecial Paste:=xlPasteValues
End If
Next
Dim rng As Range
Set rng = Range("F2:f1000")
'Loop backwards through the rows
'in the range that you want to evaluate.
For i = rng.Rows.Count To 1 Step -1
'If cell i in the range contains an "0", delete the entire row.
If rng.Cells(i).Value = "0" Then rng.Cells(i).EntireRow.Delete
'Deleting rows with at 0
Next
Application.CutCopyMode = False
'ThisWorkbook.ws1.calls(1, 22).Select
ws1.Activate
Range("A24:E24").Select
With Selection.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorDark1
.TintAndShade = -4.99893185216834E-02
.PatternTintAndShade = 0
End With
Range("A23:E24").Select
Selection.Copy
Application.CutCopyMode = False
Selection.Copy
Range("A25:E1000").Select
Selection.PasteSpecial Paste:=xlPasteFormats, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False
Range("A21").Select
'Adding grey scale to the rows to make is eazier to read.
'Else
End If
End Sub
So a basic principal of programming is that your functions/subroutines should only have one job. The first step I would take to improve your code would be breaking your code up into more subroutines using this principal. I won't go too in depth on the advantage of this because there's already loads of resources explaining why to do things this way. This thread has some good explanations, as well as draw backs to breaking your code up too much this way.
What I always do is start with a subroutine called Main() with a job that is simply to call the other functions in the program and pass variables between them as necessary. Make sure all your functions/subroutines have names that describe their purpose and then you will know exactly what your program is doing at each step of the process simply by looking at Main.
My code is supposed to select all of the items in A-H from the top of the sheet to the bottom most row containing text in the J column. However, now all it does is select the top row. This code has worked fine elsewhere for other purposes, but when I run it here it only selects the top row.
Here is the code and what it currently does. The commented out bit does the same when it is ran in the place of the other finalrow =statement.
Option Explicit
Sub FindRow()
Dim reportsheet As Worksheet
Dim finalrow As Integer
Set reportsheet = Sheet29
Sheet29.Activate
'finalrow = Cells(Rows.Count, 10).End(xlUp).Row
finalrow = Range("J1048576").End(xlUp).Row
If Not IsEmpty(Sheet29.Range("B2").Value) Then
Range(Cells(1, 1), Cells(finalrow, 8)).Select
End If
End Sub
This is the excerpt of code with a row counter that works.
datasheet.Select
finalrow = Cells(Rows.Count, 1).End(xlUp).Row
''loop through the rows to find the matching records
For i = 1 To finalrow
If Cells(i, 1) = item_code Then ''if the name in H1 matches the search name then
Range(Cells(i, 1), Cells(i, 9)).Copy ''copy columns 1 to 9 (A to I)
reportsheet.Select ''go to the report sheet
Range("A200").End(xlUp).Offset(1, 0).PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False ''find the first blank and paste info there
datasheet.Select ''go back to the data sheet and continue searching
End If
Next i
You can try this:
Option Explicit
Sub FindRow()
' always use Longs over Integers
Dim finalrow As Long: finalrow = 1
' you might not need this line tbh
Sheet29.Activate
With Sheet29
' custom find last row
Do While True
finalrow = finalrow + 1
If Len(CStr(.Range("J" & finalrow).Value)) = 0 Then Exit Do
Loop
' Len() is sometimes better then IsEmpty()
If Len(CStr(.Range("B2").Value)) > 0 Then
.Range(.Cells(1, 1), .Cells((finalrow - 1), 8)).Select
End If
End With
End Sub
So, after roughly 3 hours or more of searching google and for an answer, I can't seem to find an answer that fits my specific case. I've been grappling with a macro, and finally have it mostly working BUT getting it to copy/paste to a new sheet is vexing me to no ends. Here's the copy/paste lines (also the prior copy/paste that I tried to make work before I gave up on it) :
Sub Filtration()
Application.Goto Sheet1.Range("R1")
Application.ScreenUpdating = False
Dim writeRow As Integer
Dim percentage As Double
'to create skip conditions for row 1 & 2
counter = 1
For Each Cell In Sheets(1).Range("R:R")
'second part of skip condition
If counter > 2 Then
'creates condition to ignore blank cells or cells with a zero or negative number
If Cell.Value = "" Or Cell.Value <= 0 Then
Else
'creates a way to ignore offset cells if =< 0 (might need to add in for blank too)
If Cell.Offset(, -2).Value <= 0 Then
percentage = 0
Else
percentage = Cell.Value / Cell.Offset(, -2).Value
End If
'divide the current cell's value by the the cell one column over's value and compare
If percentage > 0.02 Then
Set Mastersheet = Worksheets("Sheet1") ' Copy From this sheet
Set Pastesheet = Worksheets("Sheet2") ' to this sheet
Cell.EntireRow.Copy ' copy the row from column O that meets that requirements (above, 1 and also win in Q)
'Pastesheet.Cells(lastRow + 1, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValuesAndNumberFormats
Dim LastRow As Long
With Pastesheet
LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row ' get last row in column "A
.Cells(LastRow + 1, 1).PasteSpecial xlPasteValuesAndNumberFormats
End With
End If
End If
End If
'final part of skip condition to ignore the two headers - has to be here to work, before next but after the last End IF
counter = counter + 1
Next
Application.ScreenUpdating = True
End Sub
The commented copy/paste just errors out on me on the writeRow part and couldn't figure out why, and searching turned up no reasons either. The second half works but just overwrites the same row over and over, and all the answers and examples I found out there claim it should work, so I'm at a loss. Does anyone have any ideas?
I guess you are after something like the code below (this section of code takes care only for the Paste section):
Dim LastRow As Long
Dim LastCell As Range
With Pastesheet
' safer way to get the last row
Set LastCell = .Cells.Find(What:="*", Lookat:=xlPart, LookIn:=xlFormulas, _
searchorder:=xlByRows, searchdirection:=xlPrevious, MatchCase:=False)
If Not LastCell Is Nothing Then
LastRow = LastCell.Row
End If
.Cells(LastRow + 1, 1).PasteSpecial xlPasteValuesAndNumberFormats
End With