Making a loop to execute same code multiple times - excel

I need some help to create a loop from my code
The code has two main functions:
Copy and paste general data to another workbook
Copy and paste employee data to another workbook
I want to make a loop of my code (code is shown below). I can make this code 15 times and it will work but I think that a loop is better. I don't have any experience with loops.
So when I press a button on my sheet it copies the general data and opens a other workbook, then it goes back tot he main workbook and copies the employee data and paste them in the other workbook.
The workbook that needs to be opened is found in range F82:F96, so first F82 then F83... and so on, until it reaches F96 and then the code must stop.
The general data is always found in row 15 & 16.
The employee data is found with the same string as the workbook that must be opened. The row after the string must me copied and paste in the other workbook. So for example (G82:DI82).
What I have
I made a code that works for 1 employee in cell(F82) the code below opens the workbook of this employee then copies the general data then find the right column and row to paste. Then I paste the data then it goes back tot he main workbook and copies the data which belongs to he employee (G82:DI82) an paste this data in the other workbook. Then it saves closes the opened workbook. The main workbook stays open.
What I expect
I need a loop to repeat the code. So first the employee which is in (F82) then the employee which in (F83) and so on.
The code:
Private Sub mUpdate_Click()
Dim General As Range
Dim employe1hours As Range
Dim employepaste As Range
Dim employepastehours As Range
Dim CurrentweekColumn As Range
Dim Currentweekpaste As Range
Dim employepath As String
Dim employe1 As String
Dim rowstr As String
Dim Foundrow As Range
Dim Currentweek As String
employepath = "J:\Planning\Medewerkers\"
Currentweek = Range("B7").Value
employe1 = Range("F82").Value
rowstr = Range("A2").Value
With ActiveWorkbook.Sheets("Planning").Range("14:14")
Set CurrentweekColumn = .find(what:=Currentweek, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=False, SearchFormat:=False)
End With
Set General = ActiveWorkbook.Sheets("Planning").Range(Cells(15, CurrentweekColumn.Column), Cells(16, CurrentweekColumn.Offset(0, 106).Column))
General.Copy
Workbooks.Open (employepath & employe1 & ".xlsm")
With ActiveWorkbook.Sheets("Blad1").Range("14:14")
Set Currentweekpaste = .find(what:=Currentweek, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=False, SearchFormat:=False)
End With
With ActiveWorkbook.Sheets("Blad1").Range("A:A")
Set Foundrow = .find(what:=rowstr, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=False, SearchFormat:=False)
End With
Set employepaste = ActiveWorkbook.Sheets("Blad1").Range(Cells(Foundrow.Row, Currentweekpaste.Column).Address)
employepaste.PasteSpecial Paste:=xlPasteFormats
employepaste.PasteSpecial Paste:=xlPasteValues
Workbooks(rowstr & ".xlsm").Activate
Set employe1hours = ActiveWorkbook.Sheets("Planning").Range(Cells(82, CurrentweekColumn.Column), Cells(82, CurrentweekColumn.Offset(0, 106).Column))
employe1hours.Copy
Workbooks(employe1 & ".xlsm").Activate
Set employepastehours = ActiveWorkbook.Sheets("Blad1").Range(Cells(Foundrow.Offset(2, 0).Row, Currentweekpaste.Column).Address)
employepastehours.PasteSpecial Paste:=xlPasteValues
ActiveWorkbook.Save
ActiveWorkbook.Close

Since we cannot do all the work for you, this should give you an idea how the loop could look like:
Option Explicit
Public Sub MyUpdateProcedure()
Dim Employees As Range 'define the range of employees
Set Employees = ThisWorkbook.Worksheets("SheetName").Range("F82:F96")
Dim CurrentWorkbook As Workbook
Const EmployePath As String = "J:\Planning\Medewerkers\"
Dim Employe As Range
For Each Employe In Employees 'loop throug all employees
'open the workbook
Set CurrentWorkbook = Workbooks.Open(EmployePath & Employe.Value & ".xlsm")
With CurrentWorkbook.Sheets("Blad1")
'your stuff here
End With
'your other stuff here
'save and close workbook
CurrentWorkbook.Close SaveChanges:=True
Next Employe
End Sub
Note that you have to avoid ActiveWorkbook and instead set the opened workbook into a variable like Set CurrentWorkbook = Workbooks.Open that you can easily use then.
Also make sure that all your Range(…) objects have a workbook/worksheet specified like ThisWorkbook.Worksheets("SheetName").Range(…) otherwise Excel guesses which worksheet you mean.
Also be aware of errors:
Set CurrentWorkbook = Workbooks.Open(EmployePath & Employe.Value & ".xlsm")
will throw an error if the workbook does not exist so you might want to catch it:
'open the workbook
Set CurrentWorkbook = Nothing 'initialize since we are in a loop!
On Error Resume Next 'next line throws an error if file not found so catch it
Set CurrentWorkbook = Workbooks.Open(EmployePath & Employe.Value & ".xlsm")
On Error GoTo 0 'always re-activate error reporting!
If Not CurrentWorkbook Is Nothing Then
'file for employee was found
With CurrentWorkbook.Sheets("Blad1")
'your stuff here
End With
'your other stuff here
'save and close workbook
CurrentWorkbook.Close SaveChanges:=True
Else
'file for employee was not found
End If

Related

VBA focus passing

trying to do in another focus with the window from the workbook from first trying to do in another focus with the window from the workbook from first
Sub Update_DHL()
Workbooks.Open Filename:=trk
Workbooks.Open Filename:=stp
Workbooks.Open Filename:=dhl
Windows(stp).Activate
Workbooks(stpfile).Activate
Range("B2").Select
ActiveCell.Formula = _
"Hi"
Range(Cells(2, 2), Cells(2, 2)).Copy
'Range(Cells(3, 2), Cells(65536, 45)).Select
'Selection.ClearContents
'Range(Cells(3, 47), Cells(65536, 74)).Select
'Selection.ClearContents
' Set wb = Workbooks("VMW Macro.xlsm") 'Name of the workbook you are copying from
' Set ws = wb.Sheets("Extract") 'Name of sheet you are copying
' DateStamp = Format(Now(), "mm-dd-yyyy hhmmss")
End Sub
Make sure you define variables for your workbooks and worksheets properly. You can then use them for your Range and Cells objects to specify in which workbook and worksheet they are. This way you don't need .Activate or .Select because the code even works if the workbook has no focus.
Make sure in your entire code there is no Range and Cells object without a workbook and worksheet specified. Either by using a variable like shown below. Or directly like ThisWorkbook.Worksheets("Sheet1").Range(…).
You only need to .Activate or .Select if you want to focus it for the user. You never need to do this to make VBA work properly (VBA can work with non-focused workbooks/worksheets).
Option Explicit
Sub Update_DHL()
'open your workbooks
On Error GoTo ERR_WB_OPEN
Dim wbTrk As Workbook
Set wbTrk = Workbooks.Open(Filename:=[truckfilePath])
Dim wbStp As Workbook
Set wbStp = Workbooks.Open(Filename:=[stopfilePath])
Dim wbDhl As Workbook
Set wbDhl = Workbooks.Open(Filename:=[dhlfilePath])
On Error GoTo 0
'define in which worksheet in those workbooks you want to work
Dim wsTrk As Worksheet
Set wsTrk = wbTrk.Worksheets("SheetName")
Dim wsStp As Worksheet
Set wsStp = wsStp.Worksheets("SheetName")
Dim wsDhl As Worksheet
Set wsDhl = wsDhl.Worksheets("SheetName")
'now work with those worksheets directly (no activate or select needed!)
wsStp.Range("B2").Formula = "=IF(SUMIF('Route Master.xls'!$C$7:$C$65536,$A2,'Route Master.xls'!$Q$7:$Q$65536)>0,TRUE,FALSE)"
wsStp.Range("B2").Copy
wsStp.Range(wsStp.Cells(2, 2), wsStp.Cells(EndRow2, 2)).PasteSpecial Paste:=xlPasteFormulas, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
' note this code does not work because `EndRow2` is nod defined
'select and activate a specific workbook/worksheet
'you do this ONLY if you want to focus it for the USER. Never do this for VBA instead work directly with the worksheets as shown above.
wbDhl.Activate
wsDhl.Select
Exit Sub
ERR_WB_OPEN:
MsgBox "One of the files could not be loaded.", vbCritical
End Sub
Don't forget to close your workbooks wbDhl.Close SaveChanges:=True/False otherwise they stay open.
See below. You can reference the workbook directly as pointed out by BigBen. In code, you never need to select ranges or activate workbooks/worksheets. You just need to reference them directly.
Notice I also added explicit declaration of types.
Dim a, b As Long
The line above will declare a as a variant and b as long
Sub Update_DHL()
Dim trk As Workbook, stp As Workbook, dhl As Workbook, wb As Workbook, wbNew As Workbook
Dim ws As Worksheet
Dim stpfile As String, DateStamp As String, strFolderpath As String
Dim EndRowTrk As Long, EndRowStp As Long, EndRowDHL As Long
Dim fileExplorer As FileDialog
Set dhl = [dhlfilePath]
Set trk = [truckfilePath]
Set stp = [stopfilePath]
stpfile = stp
Workbooks.Open Filename:=trk
Workbooks.Open Filename:=stp
Workbooks.Open Filename:=dhl
With Workbooks(stpfile).Worksheets(1)
.Range("B2").Formula = "Hi"
End With
End Sub

Creating a loop with an IF variable -- Run-time error '1004'

I'm trying to create a loop based on data in Column L. Data in Column L is formatted as Text and contains dates. If a match is found, the entire Row has to be highlighted in yellow.
Sub Forn()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets(1)
Dim strSearchText As String
strSearchText = Format(DateAdd("m", 2, Now()), "yyyymm")
Dim rngSearchArea As Range
Set rngSearchArea = ws.Range(Range("L10"), ws.Range("L" & ws.Range("L:L").Cells.Count).End(xlUp))
Dim strFirstFound As String
Dim rngCurrentFound As Range
Set rngCurrentFound = ws.Range("L10")
Set rngCurrentFound = rngSearchArea.Find(What:=strSearchText, After:=rngCurrentFound, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=True, SearchFormat:=False)
If rngCurrentFound Is Nothing Then
MsgBox "INGEN TREFF"
Exit Sub
End If
rngCurrentFound.Resize(1, 16).Offset(0, -11).Interior.ColorIndex = 6
strFirstFound = rngCurrentFound.Address
Dim rngSource As Range
Dim rngNextFound As Range
Do
Set rngNextFound = rngSearchArea.FindNext(rngCurrentFound)
If rngNextFound.Row > rngCurrentFound.Row Then
rngCurrentFound.Resize(1, 16).Offset(0, -11).Interior.ColorIndex = 6
Else
Set rngSource = ws.Range(rngCurrentFound, ws.UsedRange.Cells(ws.UsedRange.Cells.Count))
End If
Set rngCurrentFound = rngSearchArea.FindNext(rngCurrentFound)
Loop While rngCurrentFound.Address <> strFirstFound
End Sub
I'm getting Run-time error '1004':
Method 'Range' of object '_Worksheet' failed.
This exact same code worked for me yesterday and I'm at a complete loss as to what needs to be changed here.
UPD: the error is caused by launching the Macro from PERSONAL.XLSB --
Set ws = ThisWorkbook.Worksheets(1)
no longer works as needed.
Using ThisWorkbook in a macro tells VBA that you want to access the workbook where the code is stored. If you move your code to the Personal.xlsb, it will access Personal.xlsb and it's sheets - and that's probably not what you want.
You put a macro in the Personal usually to have it available all the time, e.g. via a Keyboard shortcut.
If you want the code to work on the currently visible workbook, replace ThisWorkbook simply by ActiveWorkbook - but you need to ensure that it makes sense to run the macro on that workbook.
If you want to execute it always on the same workbook, it makes no sense to have it in the personal - just keep it in the workbook itself.
If you want to select the workbook first, add a fileopen-dialog to your code, open the workbook using Workbooks.Open and use the workbook-reference returned by the open-command.

How to move rows from one workbook to another (no select)

My goal is to take an excel document with variable row size, copy it and then paste it onto the bottom row of a new document.
Longer story, I need to take monthly sales reports and stack them into a larger excel file. Each month we make a variable number of sales. I need to aggregate all of these months together so we can process them.
I have some code that I thought worked below. It was able to move variable rows within different work sheets, but could not do the same for different work books.
Private Sub MoveRowToEndOfTable()
Dim LastRow As Long
LastRow = Cells.Find(What:="*", SearchOrder:=xlByRows,SearchDirection:=xlPrevious).Row
Sheets(1).Range("A2:A" & LastRow, "G2:G" & LastRow).Copy
Workbooks("BRN report Aggregator.xlsx").Worksheets("New shares EOM").Range("a6000").End(xlUp).Offset(1, 0).Cells.Insert
End Sub
I guess that your workbook is closed, check it before paste values (if workbook is closed ~> open it) :
Private Sub MoveRowToEndOfTable()
Dim LastRow As Long
LastRow = Cells.Find(What:="*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Sheets(1).Range("A2:A" & LastRow, "G2:G" & LastRow).Copy
Dim wb As Workbook, wb_target As Workbook
'check if workbook is open already
For Each wb In Workbooks
If wb.Name = "BRN report Aggregator.xlsx" Then
Set wb_target = Workbooks("BRN report Aggregator.xlsx")
Exit For
End If
Next wb
'if not then open it
If wb_target Is Nothing Then
Set wb = Workbooks.Open("Path_to_file/BRN report Aggregator.xlsx")
End If
wb.Worksheets("New shares EOM").Range("a6000").End(xlUp).Offset(1, 0).PasteSpecial xlPasteAll 'or xlPasteValues --depends on your needs
wb.Close True 'save and close if required
End Sub

Excel VBA PasteSpecial not pasting consistently

I have a report which is used to import data relating to jobplans and then creates graphs and stats based on the data. Calculations and graphs are based on tables and the tables are populated by VBA - user selects the file and then VBA checks it matches the expected file format and put everything in the right place.
HOWEVER, the pastespecial part of the code does not paste everything correctly. Specifically there are a number of columns with datevalues and when pasted some of them (not one column or particular rows but seemingly random cells) are not formatted as dates when pasted and therefore are not captured in formulas when I look for job within particular timeframes.
In the source file the all data is 100% saved as a datevalue (if I put a filter on the data, it is all grouped by year and can be expanded to month/day/time + if I use a test cell to do add 1 to the cells that the next date is shown). Once pasted into target sheet then some is still a datevalue but some appears to be text and showing as dd/mm/yyyy hh:mm but being missed from calculation. On these cells if I go onto them press F2 and then Enter then the cell changes to a datevalue (realigns to the right and then gets included in daterange formulas).
Here is the code:
Public Sub importdata()
Dim wb1, wb3 As Workbook
Dim ws1, ws3 As Worksheet
Dim lrow As Long
Dim WOtable As ListObject
Dim searchcell As Range
Set wb1 = ThisWorkbook
Set ws1 = wb1.Sheets("Dashboard")
Set WOtable = ws1.ListObjects("workorder")
WOfile = Application.GetOpenFilename(FileFilter:="Excel Files (*.CSV),*.CSV", Title:="Select Workorder Extract To Be Opened",MultiSelect:=False)
If WOfile = False Then Exit Sub
Set wb3 = Workbooks.Open(WOfile)
Set ws3 = wb3.Sheets(1)
ws3.Range("M:M, O:O, Q:Q").EntireColumn.Delete
If ws3.Range("A1").Value = "jobnumber" And ws3.Range("B1").Value ="jobdesc" And etc etc Then
lrow = ws3.Range("A1").End(xlDown).Row
ws3.Range("A2:O" & lrow).Copy
WOtable.DataBodyRange(1, 1).PasteSpecial Paste:=xlPasteValuesAndNumberFormats, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False
Else: MsgBox ("File selected to import workorder information was not in expected format, please check the file and retry.")
End If
wb3.Close False
End Sub
I have tried to add the following line before copying to force it based on something I saw on google but to no avail:
ws3.Columns("E:K").NumberFormat = "DD/MM/YYYY HH:MM:SS"
Thanks for any help
As discussed in comments, an example usage of pushing the data into a variant array and then pasting it to the destination. A few comments:
Always state what type you want for each variable, comma separated variables on the same line don't all take the last type.
Use with statements to keep code slightly cleaner and reduce the amount of references excel needs to resolve.
As you didn't clear the contents of the table (merely overwrote them) I replicated this behaviour in the code as I assume it is intended.
Edited sub:
Public Sub importdata()
Dim wb1 As Workbook, wb3 As Workbook
Dim ws1 As Worksheet, ws3 As Worksheet
Dim WOtable As ListObject
Dim varTMP As Variant
Set wb1 = ThisWorkbook
Set ws1 = wb1.Sheets("Dashboard")
Set WOtable = ws1.ListObjects("workorder")
WOfile = Application.GetOpenFilename(FileFilter:="Excel Files (*.CSV),*.CSV", Title:="Select Workorder Extract To Be Opened", MultiSelect:=False)
If WOfile = False Then Exit Sub
Set wb3 = Workbooks.Open(WOfile)
Set ws3 = wb3.Sheets(1)
With ws3
.Range("M:M, O:O, Q:Q").EntireColumn.Delete
If .Range("A1").Value = "jobnumber" And .Range("B1").Value ="jobdesc" And etc etc Then
'load data into variant array
varTMP = .Cells(1, 1).CurrentRegion
'If you want to do any data manipulation on the array, do it here
'Paste array
End With
With WOtable.DataBodyRange
Range(.Cells(1, 1), .Cells(0 + UBound(varTMP, 1), 0 + UBound(varTMP, 2))) = varTMP
End With
Else
MsgBox ("File selected to import workorder information was not in expected format, please check the file and retry.")
End If
wb3.Close False
End Sub

Excel vba code for updating workbook from external worksheet

Yet another worksheet copying problem! This is a simple problem that has got me stumped. I want the click of a command button (in action.xlsm) to repopulate the values in a range ("stock" - 2 cols & maybe 100 rows - this is the master inventory records) in a separate excel file (inventory.xlsx), from a named range ("newInventory" - same size as other named range) in the active worksheet (in action.xlsm) that has had the original "stock" values reduced by the values of items taken out of stock. The calculations are OK I just can't get the master inventory file to update. I have checked heaps of forums and tried two approaches to no avail. I have tried:
Private Sub CommandButton1_Click()
Dim InventoryFileName As String
InventoryFileName = "C:\Users\david\Documents\inventory.xlsx"
Workbooks(InventoryFileName).Worksheets("Sheet1").Range("stock") = ThisWorkbook.Worksheets("inventory").Range("newInventory").Value
Workbooks(InventoryFileName).Save
End Sub
Throws a "Run-time error '9': Subscript out of range" on line 4. I have also tried:
Private Sub CommandButton1_Click()
Dim wbTarget As Workbook 'workbook where the data is to be pasted
Dim wsTarget As Worksheet
Dim wbThis As Workbook 'workbook from where the data is to copied
Dim wsThis As Worksheet
Dim strName As String 'name of the source sheet/ target workbook
'set to the current active workbook (the source book)
Set wbThis = ActiveWorkbook
Set wsThis = ActiveSheet
'get the active sheetname of the book
strName = wsThis.Name
'open a workbook that has same name as the sheet name
Set wbTarget = Workbooks.Open("C:\Users\david\Documents\" & strName & ".xlsx")
Set wsTarget = wbTarget.Worksheets("Sheet1")
'select cell A1 on the target book
wbTarget.wsTarget.Range("A1").Select
'clear existing values form target book
wbTarget.wsTarget.Range("A1:B10").ClearContents
'activate the source book
wbThis.Activate
'clear any thing on clipboard to maximize available memory
Application.CutCopyMode = False
'copy the range from source book
wbThis.wsThis.Range("A1:B10").Copy
'paste the data on the target book
wbTarget.wsTarget.Range("A1").PasteSpecial Paste:=xlPasteValues
'clear any thing on clipboard to maximize available memory
Application.CutCopyMode = False
'save the target book
wbTarget.Save
'close the workbook
wbTarget.Close
'activate the source book again
wbThis.Activate
'clear memory
Set wbTarget = Nothing
Set wbThis = Nothing
End Sub
This throws a "Run-time error '438': Object doesn't support this property or method" on line wbTarget.wsTarget.Range("A1").Select
What have I got wrong? Any suggestions?
Replace
wbTarget.wsTarget.Range("A1").Select
with just
wsTarget.Range("A1").Select
The workbook is already implied from the way you defined wsTarget. I suspect that will do it. If you run the code in the debugger, then when you do a "watch" on the variable you can see exactly what does and doesn't work..
Firstly you have 2 commandbutton1. Secondly you must have a reference for a Range like:
Workbooks(InventoryFileName).Worksheets("Sheet1").Range("A3:B21") = ThisWorkbook.Worksheets("inventory").Range("A10:B12").Value
or
stock="A3:B21"
newInventory="A10:B12"

Resources