Related
I've been working on the code below for a while now and I'm almost done. It's taking 3 cells of data from one sheet, copying it in another, saving a copy based on the name in the first sheet and then looping until completed for all filled rows.
The snag I'm hitting is that when the first loop completes and it needs to select the WB that holds the data (the selection is needed for the function) it can't select it due to a fault in WsStam.Cells(row, iKolomnrCorpID).EntireRow.Select. When I debug, switch to the WB and run code it does work.
It's probably something stupid I'm missing. I appreciate your help!
Sub motivatieFormOpmaken()
Dim wbMotivTemp As Workbook
Dim wsMotiv As Worksheet
Dim PathOnly, mot, FileOnly As String
Dim StrPadSourcenaam As String
Dim WsStam As Worksheet
Dim WbStam As Workbook
Dim LastRow As Long
Set wbMotivTemp = ThisWorkbook
Set wsMotiv = ActiveSheet
StrHoofdDocument = ActiveWorkbook.Name
StrPadHoofdDocument = ActiveWorkbook.Path
StrPadSourcenaam = StrPadHoofdDocument & "\" & c_SourceDump
If Not FileThere(StrPadSourcenaam) Then
MsgBox "Document " & StrPadSourcenaam & " is niet gevonden."
Exit Sub
End If
Application.ScreenUpdating = False
Workbooks.Open FileName:=StrPadSourcenaam
Set WbStam = ActiveWorkbook
Set WsStam = WbStam.Worksheets("Stambestand")
Application.Run "Stambestand.xlsm!unhiderowsandcolumns"
Worksheets("stambestand").Activate
iLaatsteKolom = Worksheets("stambestand").Cells.SpecialCells(xlLastCell).Column
iLaatsteRij = Worksheets("stambestand").Cells.SpecialCells(xlLastCell).row
VulKolomNr
If KolomControle = False Then Exit Sub
Cells(1, iKolomnrVerwijderen_uit_de_tellingen).AutoFilter Field:=iKolomnrVerwijderen_uit_de_tellingen, Criteria1:="0"
LastRow = Cells(1, iKolomnrCorpID).End(xlDown).row
Dim row As Long
row = 2
With WsStam
Do Until row > iLaatsteRij
If .Cells(row, iKolomnrCorpID).RowHeight > 0 Then
WsStam.Cells(row, iKolomnrCorpID).EntireRow.Select 'It crashes at this line, after the first loop
wsMotiv.Range("motiv_cid") = Cells(row, iKolomnrCorpID).Text
wsMotiv.Range("motiv_naam") = Cells(row, iKolomnrNaam).Text
wsMotiv.Range("motiv_ldg") = Cells(row, iKolomnrHuidigeLeidingGevende).Text
n = naamOpmaken
wbMotivTemp.Activate
ActiveWorkbook.SaveAs FileName:=StrPadHoofdDocument & "\Docs\" & n & ".xlsm", FileFormat:=xlOpenXMLWorkbookMacroEnabled, CreateBackup:=False
End If
row = row + 1
Loop
End With
End Sub
Function naamOpmaken() As String
Dim rng As Range
Dim row As Range
Set rng = Selection.SpecialCells(xlCellTypeVisible)
iRijnummer = rng.row
If iRijnummer > 1 Then
naam = Cells(iRijnummer, iKolomnrNaam).Text
ldg = Cells(iRijnummer, iKolomnrHuidigeLeidingGevende).Text
cid = Cells(iRijnummer, iKolomnrCorpID).Text
Dim Position As Long, Length As Long
Dim n As String
Position = InStrRev(naam, " ")
Length = Len(naam)
n = Right(naam, Length - Position)
End If
naamOpmaken = n + "-" + ldg + "-" + cid
End Function
you have to activate a worksheet before selecting a cell of
since you're jumping between sheets you have to add
WsStam.Activate
right before
WsStam.Cells(row, iKolomnrCorpID).EntireRow.Select
BTW, you don't seem to need that selection at all so you may want to try and comment that line!
Hopefully you may find this useful for the future.
I've had a look through your code and made some updates so you shouldn't have to select any sheets and that problem line is removed completely. I've also added a new function at the bottom which will find the last cell on any sheet you reference.
Option Explicit 'Very important at top of module.
'Ensures all variables are declared correctly.
Sub motivatieFormOpmaken()
Dim wbMotivTemp As Workbook
Dim wsMotiv As Worksheet
' Dim PathOnly, mot, FileOnly As String
'''''''''''''''''''
'New code.
Dim PathOnly As String, mot As String, FileOnly As String
'''''''''''''''''''
Dim StrPadSourcenaam As String
'''''''''''''''''''
'New code.
Dim StrHoofdDocument As String
Dim StrPadHoofdDocument As String
Dim c_SourceDump As String
c_SourceDump = "MyFileName.xlsx"
Dim KolomControle As Boolean
'''''''''''''''''''
Dim WsStam As Worksheet
Dim WbStam As Workbook
Dim LastRow As Long
Set wbMotivTemp = ThisWorkbook
Set wsMotiv = ActiveSheet
StrHoofdDocument = ActiveWorkbook.Name
StrPadHoofdDocument = ActiveWorkbook.Path
StrPadSourcenaam = StrPadHoofdDocument & "\" & c_SourceDump
If Not FileThere(StrPadSourcenaam) Then
MsgBox "Document " & StrPadSourcenaam & " is niet gevonden."
Else
' Exit Sub
' End If
Application.ScreenUpdating = False
' Workbooks.Open Filename:=StrPadSourcenaam
' Set WbStam = ActiveWorkbook
'''''''''''''''''''
'New code.
Set WbStam = Workbooks.Open(Filename:=StrPadSourcenaam)
'''''''''''''''''''
Set WsStam = WbStam.Worksheets("Stambestand")
' Application.Run "Stambestand.xlsm!unhiderowsandcolumns"
'''''''''''''''''''
'New code as possible replacement for "unhiderowsandcolumns"
WsStam.Cells.EntireColumn.Hidden = False
WsStam.Cells.EntireRow.Hidden = False
'''''''''''''''''''
' Worksheets("stambestand").Activate
' iLaatsteKolom = Worksheets("stambestand").Cells.SpecialCells(xlLastCell).Column
' iLaatsteRij = Worksheets("stambestand").Cells.SpecialCells(xlLastCell).row
'''''''''''''''''''
'New code. You may want to check for filters before finding last row?
iLaatsteKolom = LastCell(WsStam).Column
iLaatsteRij = LastCell(WsStam).row
'''''''''''''''''''
VulKolomNr 'No idea - getting deja vu here.
' If KolomControle = False Then Exit Sub
'''''''''''''''''''
'New code.
If KolomControle Then
'''''''''''''''''''
WsStam.Cells(1, iKolomnrVerwijderen_uit_de_tellingen).AutoFilter Field:=iKolomnrVerwijderen_uit_de_tellingen, Criteria1:="0"
' LastRow = Cells(1, iKolomnrCorpID).End(xlDown).row
'''''''''''''''''''
'New code. The function will return the last filtered row.
LastRow = LastCell(WsStam).row
'''''''''''''''''''
Dim row As Long
row = 2
With WsStam
Do Until row > iLaatsteRij
If .Cells(row, iKolomnrCorpID).RowHeight > 0 Then
'''''''''''''''''''
'I don't think you even need this line.
' WsStam.Cells(row, iKolomnrCorpID).EntireRow.Select 'It crashes at this line, after the first loop
' wsMotiv.Range("motiv_cid") = Cells(row, iKolomnrCorpID).Text
' wsMotiv.Range("motiv_naam") = Cells(row, iKolomnrNaam).Text
' wsMotiv.Range("motiv_ldg") = Cells(row, iKolomnrHuidigeLeidingGevende).Text
'''''''''''''''''''
'New code. Note the "." before "Cells" which tells it that cell is on "WsStam" (in the "With")
' Also formatting the cell to text - will need to update as required.
wsMotiv.Range("motiv_cid") = Format(.Cells(row, iKolomnrCorpID), "0000")
wsMotiv.Range("motiv_naam") = Format(.Cells(row, iKolomnrNaam), "0000")
wsMotiv.Range("motiv_ldg") = Format(.Cells(row, iKolomnrHuidigeLeidingGevende), "0000")
'Do you mean this to save on each loop?
' n = naamOpmaken
' wbMotivTemp.Activate
' ActiveWorkbook.SaveAs Filename:=StrPadHoofdDocument & "\Docs\" & n & ".xlsm", FileFormat:=xlOpenXMLWorkbookMacroEnabled, CreateBackup:=False
'''''''''''''''''''
'New code. Combines the above three lines.
wbMotivTemp.SaveAs Filename:=StrPadHoofdDocument & "\Docs\" & naamOpmaken(WsStam) & ".xlsm", FileFormat:=xlOpenXMLWorkbookMacroEnabled, CreateBackup:=False
End If
row = row + 1
Loop
End With
'''''''''''''''''''
'New code. End of "If KolomControle" block.
End If
'''''''''''''''''''
''''''''''''''''
'New code - end of "If Not FileThere" block.
'Give procedure a single exit point.
End If
End Sub
'Added the worksheet as an argument to the procedure.
'This is then passed from the main procedure and you don't need to select the sheet first.
Function naamOpmaken(wrkSht As Worksheet) As String
Dim rng As Range
Dim row As Range
Set rng = Selection.SpecialCells(xlCellTypeVisible)
'''''''''''''''''''
'New code
Dim naam As String
Dim ldg As String
Dim cid As String
'''''''''''''''''''
iRijnummer = rng.row
If iRijnummer > 1 Then
' naam = Cells(iRijnummer, iKolomnrNaam).Text
' ldg = Cells(iRijnummer, iKolomnrHuidigeLeidingGevende).Text
' cid = Cells(iRijnummer, iKolomnrCorpID).Text
'''''''''''''''''''
'New code - not reference to the worksheet, and using default value of cell.
' may need to add "FORMAT" to get numericals in correct format.
naam = wrkSht.Cells(iRijnummer, iKolomnrNaam)
ldg = wrkSht.Cells(iRijnummer, iKolomnrHuidigeLeidingGevende)
cid = wrkSht.Cells(iRijnummer, iKolomnrCorpID)
'''''''''''''''''''
Dim Position As Long, Length As Long
Dim n As String
Position = InStrRev(naam, " ")
Length = Len(naam)
n = Right(naam, Length - Position)
End If
'If n and ldg are numbers this will add them rather than stick them together.
' naamOpmaken = n + "-" + ldg + "-" + cid
''''''''''''''''
'New code
naamOpmaken = n & "-" & ldg & "-" & cid
''''''''''''''''
End Function
'New function to find last cell containing data on sheet.
Public Function LastCell(wrkSht As Worksheet) As Range
Dim lLastCol As Long, lLastRow As Long
On Error Resume Next
With wrkSht
lLastCol = .Cells.Find("*", , , , xlByColumns, xlPrevious).Column
lLastRow = .Cells.Find("*", , , , xlByRows, xlPrevious).row
If lLastCol = 0 Then lLastCol = 1
If lLastRow = 0 Then lLastRow = 1
Set LastCell = wrkSht.Cells(lLastRow, lLastCol)
End With
On Error GoTo 0
End Function
I am generating a new workbook from a multiple workbooks, i can generate a summary of all the errors found, but when i try to copy the sheets with the error information i got the runtime error 9
These is the line failing
If exists = True Then
ActiveWorkbook.Sheets(sheetName).Copy After:=ThisWorkbook.Sheets(1)
End If
Other thing i havent add is that all the sheets on the multiple files have the same names, so i want to know if there is a way that the sheet when is copy i can add the file name and the sheet name
Sub getViolations()
Path = "C:\Users\omartinr\Desktop\New folder (4)\New folder\"
Filename = Dir(Path & "*.xls")
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
Set TxtRng = ws.Range("A1:N1")
TxtRng.Font.ColorIndex = 2
TxtRng.Interior.ColorIndex = 5
TxtRng.Value = [{"Partition Name","Tag","EM Supply","SH Signal","PK","Sfactor","FiSH","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL"}]
TxtRng.HorizontalAlignment = xlCenter
Dim i As Integer
i = 2
Do While Filename <> ""
Workbooks.Open Filename:=Path & Filename, ReadOnly:=True
Dim wc As Worksheet
Set wc = ActiveWorkbook.Sheets("Violations Summary")
ws.Cells(i, 1).Value = ActiveWorkbook.Sheets("Violations Summary").Range("B1")
ws.Cells(i, 2).Value = ActiveWorkbook.Sheets("Violations Summary").Range("C1")
Dim count As Integer
count = 15
Dim sheetName As String, mySheetNameTest As String
Dim n As Integer
Dim exits As Boolean
For n = 3 To 14
If Not IsEmpty(wc.Cells(n, 2)) Then
If (wc.Cells(n, 2)) = 0 Then
ws.Cells(i, n).Font.ColorIndex = 4
ws.Cells(i, n).Value = wc.Cells(n, 2)
End If
If (wc.Cells(n, 2)) > 0 Then
Select Case wc.Cells(n, 1)
Case "PK"
sheetName = "Peak"
Case "Sfactor"
sheetName = "SF Supply"
Case Else
sheetName = wc.Cells(n, 1)
End Select
exists = sheetExists(sheetName)
If exists = True Then
ActiveWorkbook.Sheets(sheetName).Copy After:=ThisWorkbook.Sheets(1)
End If
ws.Cells(i, count) = wc.Cells(1, n).Value
ws.Cells(i, n).Font.ColorIndex = 3
ws.Cells(i, n).Value = wc.Cells(n, 2)
End If
If (ActiveWorkbook.Sheets("Violations Summary").Cells(n, 2)) < 0 Then
ws.Cells(i, n).Font.ColorIndex = 3
ws.Cells(i, n).Value = wc.Cells(n, 2)
End If
End If
If IsEmpty(wc.Cells(n, 2)) Then
ws.Cells(i, n).Value = ["NA"]
End If
count = count + 1
Next n
Workbooks(Filename).Close
Filename = Dir()
i = i + 1
Loop
End Sub
Function sheetExists(sheetToFind As String) As Boolean
sheetExists = False
For Each Sheet In Worksheets
If sheetToFind = Sheet.Name Then
sheetExists = True
Exit Function
End If
Next Sheet
End Function
Put option explicit at top so spelling of variables is checked and that they are declared. The variable exists was mispelt and there were a number of other variables not declared. I have put some other comments in with the code.
Some of the logic i think can be simplified and i have given some examples. Also, ensure consistent use of named variable wc. If nothing else it should be easier to debug now. Compiles on my machine so give it a try.
This all works on the assumption that each workbook you open has the "Violations Summary" sheet and it is spelt as shown.
You have the filename already stored in the variable Filename so you can use (concatenate?) that with the sheetname variable.
Option Explicit 'Set this to ensure all variable declared and consistent spelling
'Consider using WorkSheets collection rather than Sheets unless you have chart sheets as well?
Sub getViolations()
Dim Path As String 'Declare you other variables
Dim FileName As String
Path = "C:\Users\omartinr\Desktop\New folder (4)\New folder\"
FileName = Dir(Path & "*.xls")
Dim ws As Worksheet
Dim TxtRng As Range 'Declare this
Set ws = ThisWorkbook.Sheets("Sheet1")
Set TxtRng = ws.Range("A1:N1")
TxtRng.Font.ColorIndex = 2
TxtRng.Interior.ColorIndex = 5
TxtRng.Value = [{"Partition Name","Tag","EM Supply","SH Signal","PK","Sfactor","FiSH","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL"}]
TxtRng.HorizontalAlignment = xlCenter
Dim i As Integer
i = 2
Do While FileName <> ""
Workbooks.Open FileName:=Path & FileName, ReadOnly:=True
Dim wc As Worksheet 'Consider whether to place these declarations just before the loop, avoids risk others may think there will be reinitialization even though there isn't
Set wc = ActiveWorkbook.Sheets("Violations Summary")
ws.Cells(i, 1).Value = wc.Range("B1") 'Use the wc variable
ws.Cells(i, 2).Value = wc.Range("C1")
Dim count As Integer
Dim sheetName As String, mySheetNameTest As String
Dim n As Integer
Dim exists As Boolean 'Corrected spelling
count = 15
For n = 3 To 14
If Not IsEmpty(wc.Cells(n, 2)) Then
If (wc.Cells(n, 2)) = 0 Then
ws.Cells(i, n).Font.ColorIndex = 4
ws.Cells(i, n).Value = wc.Cells(n, 2)
End If
If (wc.Cells(n, 2)) > 0 Then
Select Case wc.Cells(n, 1)
Case "PK"
sheetName = "Peak"
Case "Sfactor"
sheetName = "SF Supply"
Case Else
sheetName = wc.Cells(n, 1)
End Select
exists = sheetExists(sheetName)
If exists Then 'Shortened by removing = True (evaluates in same way)
ActiveWorkbook.Sheets(sheetName).Copy After:=ThisWorkbook.Sheets(1)
End If
ws.Cells(i, count) = wc.Cells(1, n).Value
ws.Cells(i, n).Font.ColorIndex = 3
ws.Cells(i, n).Value = wc.Cells(n, 2)
End If
If (wc.Cells(n, 2)) < 0 Then 'used wc variable
ws.Cells(i, n).Font.ColorIndex = 3
ws.Cells(i, n).Value = wc.Cells(n, 2)
End If
Else 'Simplified this as if is not empty then is empty so can use else
ws.Cells(i, n).Value = ["NA"] 'what is pupose of square brackets? These can be removed i think
End If
count = count + 1
Next n
Workbooks(FileName).Close
FileName = Dir()
i = i + 1
Loop
End Sub
Function sheetExists(sheetToFind As String) As Boolean
Dim Sheet As Worksheet ' declare
sheetExists = False
For Each Sheet In Worksheets
If sheetToFind = Sheet.Name Then
sheetExists = True
Exit Function
End If
Next Sheet
End Function
After you copy the ActiveWorkbook.Sheets(sheetName) to ThisWorkbook, ThisWorkbook becomes the ActiveWorkbook. ActiveWorkbook.Sheets(sheetName).Copy After:=ThisWorkbook.Sheets(1) should not throw an error but will probably cause ActiveWorkbook.Sheets("Violations Summary") to fail. For this reason, you should always fully qualify your references.
Some idealist programmers say that a subroutine should perform 1 simply task. Personally, I believe that if you have to scroll up, down, left or right to see what your code is doing it is time to refactor it. When refactoring I try to extract logical groups of tasks in a separate subroutine. This makes debugging and modifying the code far easier.
Refactored Code
Option Explicit
Sub getViolations()
Const Path As String = "C:\Users\omartinr\Desktop\New folder (4)\New folder\"
Dim n As Long
Dim Filename As String
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
Sheet1Setup ws
Filename = Dir(Path & "*.xls")
Do While Filename <> ""
ProcessWorkbook Filename, ws.Rows(n)
Filename = Dir()
Loop
End Sub
Sub ProcessWorkbook(WBName As String, row As Range)
Dim nOffset As Long, n As Long
Dim sheetName As String
Dim WB As Workbook
Set WB = Workbooks.Open(Filename:=Path & Filename, ReadOnly:=True)
With WB.Sheets("Violations Summary")
row.Columns(1).Value = .Range("B1")
row.Columns(2).Value = .Range("C1")
nOffset = 12
For n = 3 To 14
If .Cells(n, 2) = "" Then
row.Columns(n).Value = ["NA"]
ElseIf (.Cells(n, 2)) = 0 Then
row.Columns(n).Font.ColorIndex = 4
row.Columns(n).Font.ColorIndex = 0
ElseIf (.Cells(n, 2)) = 0 Then
Select Case wc.Cells(n, 1)
Case "PK"
sheetName = "Peak"
Case "Sfactor"
sheetName = "SF Supply"
Case Else
sheetName = wc.Cells(n, 1)
End Select
'Range.Parent refers to the ranges worksheet. row.Parent refers to ThisWorkbook.Sheets(1)
If SheetExists(WB, sheetName) Then .Copy After:=row.Parent.Sheets(1)
row.Columns(n + nOffset) = .Cells(1, n).Value
row.Columns(n).Font.ColorIndex = 3
row.Columns(n).Value = .Cells(n, 2)
End If
Next
End With
WB.Close SaveChanges:=False
End Sub
Function SheetExists(WB As Workbook, sheetToFind As String) As Boolean
Dim ws As Worksheet
For Each ws In WB.Worksheets
If sheetToFind = ws.Name Then
SheetExists = True
Exit Function
End If
Next
End Function
Sub Sheet1Setup(ws As Worksheet)
With ws.Range("A1:N1")
.Value = [{"Partition Name","Tag","EM Supply","SH Signal","PK","Sfactor","FiSH","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL","RESCAL"}]
.Font.ColorIndex = 2
.Interior.ColorIndex = 5
.HorizontalAlignment = xlCenter
End With
End Sub
Note: row is the target Row of ThisWorkbook.Sheets(1). row.Columns(3) is a fancy way to write row.Cells(1, 3) which refers to the 3rd cell in the target row. Also note that Cells, Columns, and Rows are all relative to the range they belong to. e.g. Range("C1").Columns(2) refers to D1, Range("C1").Rows(2).Columns(2) refers to D2, Range("C1").Cells(2,2) also refers to D2.
Each month I get our sales report and it contains quantities of goods we sold along with product details, and I created a template using vba where user can specify a product and it can create a excel report for them.
However, I would like to expand/modify so if I have multiple excel reports instead of just one report. I would like excel to separate however many product codes I input or listed.
Now, I added a tab called list in my template which I can list the # of product codes (the 4 digit number, in column A) where vba should read from but I need help on modifying the codes so instead of asking the user, it reads the list instead. Secondly, since master file contains all of the products and I maybe just need 20 or 30 of them, I will need the vba codes to be flexible as possible.
The way i set it up, I am basically updating/copying new info from Master file into Monthly Template and re-saving Monthly Template as product codes product as of 9.1.2017 file.
Sub monthly()
Dim x1 As Workbook, y1 As Workbook
Dim ws1, ws2 As Worksheet
Dim LR3, LR5 As Long
Dim ws3 As Worksheet
Dim Rng3, Rng4 As Range
Dim x3 As Long
Set x1 = Workbooks("Master.xlsx")
Set y1 = Workbooks("Monthly Template.xlsm")
Set ws1 = x1.Sheets("Products")
Set ws2 = y1.Sheets("Products")
Set ws3 = y1.Sheets("List")
ws2.Range("A3:AA30000").ClearContents
ws1.Cells.Copy ws2.Cells
x1.Close True
LR5 = ws3.Cells(Rows.Count, "A").End(xlUp).Row
With y1.Sheets("List")
Range("A1:A32").Sort key1:=Range("A1"), Order1:=xlAscending
End With
LR3 = ws2.Cells(Rows.Count, "A").End(xlUp).Row
Set Rng3 = ws2.Range("AC3:AC" & LR3)
Set Rng4 = ws3.Range("A1:A" & LR5)
For n = 3 To LR3
ws2.Cells(n, 29).FormulaR1C1 = "=LEFT(RC[-21], 4)"
Next n
With y1.Sheets("List")
j = .Cells(.Rows.Count, 1).End(xlUp).Row
End With
With ws2
l = .Cells(.Rows.Count, 1).End(xlUp).Row
End With
For i = 1 To j
For k = 3 To l
If Sheets("List").Cells(i, 1).Value = Sheets("Products").Cells(k, 29).Value Then
With Sheets("Output")
m = .Cells(.Rows.Count, 1).End(xlUp).Row
End With
Sheets("Output").Rows(m + 1).Value = Sheets("Products").Rows(k).Value
End If
Next k
Next i
Sheets("Output").Columns("AC").ClearContents
Dim cell As Range
Dim dict As Object, vKey As Variant
Dim Key As String
Dim SheetsInNewWorkbook As Long
Dim DateOf As Date
DateOf = DateSerial(Year(Date), Month(Date), 1)
With Application
.ScreenUpdating = False
SheetsInNewWorkbook = .SheetsInNewWorkbook
.SheetsInNewWorkbook = 1
End With
Set dict = CreateObject("Scripting.Dictionary")
With ThisWorkbook.Worksheets("List")
For Each cell In .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
Key = Left(cell.Value, 4)
'Store an ArrayList in the Scripting.Dictionary that can be retrieved using the Product Key
If Not dict.exists(Key) Then dict.Add Key, CreateObject("System.Collections.ArrayList")
Next
End With
With Workbooks("Monthly Template.xlsm").Worksheets("Output")
For Each cell In .Range("H2", .Range("A" & .Rows.Count).End(xlUp))
Key = Left(cell.Value, 4)
'Add the Products to the ArrayList in the Scripting.Dictionary that is associated with the Product Key
If dict.exists(Key) Then dict(Key).Add cell.Value
Next
End With
For Each vKey In dict
If dict(vKey).Count > 0 Then
With Workbooks.Add
With .Worksheets(1)
.Name = "Products"
' .Range("A1").Value = "Products"
Workbooks("Monthly Template.xlsm").Worksheets("Output").Cells.Copy Worksheets(1).Cells
For Z = 1 To LR5
For x3 = Rng3.Rows.Count To 1 Step -1
If InStr(1, Rng3.Cells(x3, 1).Text, Workbooks("Monthly Template.xlsm").Worksheets("List").Cells(Z, 1).Text) = 0 Then
Rng3.Cells(x3, 1).EntireRow.Delete
End If
Next x3
Next Z
'.Range("A2").Resize(dict(vKey).Count).Value = Application.Transpose(dict(vKey).ToArray)
End With
.SaveAs Filename:=getMonthlyFileName(DateOf, CStr(vKey)), FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
.Close SaveChanges:=False
End With
End If
Next
With Application
.ScreenUpdating = True
.SheetsInNewWorkbook = SheetsInNewWorkbook
End With
End Sub
Function getMonthlyFileName(DateOf As Date, Product As String) As String
Dim path As String
path = ThisWorkbook.path & "\Product Reports\"
If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
path = path & Format(DateOf, "yyyy") & "\"
If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
path = path & Format(DateOf, "mmm") & "\"
If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
getMonthlyFileName = path & "Product - " & Product & Format(DateOf, " mmm.dd.yyyy") & ".xlsx"
End Function
I seen no reason why to save copies of Monthly Template.xlsm. The OP's code simply creates a list on a worksheet and saves it to file. I might be some formatting missing that would normally get saved over from the Master File.
getMonthlyFileName(DateOf, Product) - creates a file path (Root Path\Year of Date\Month of Date\Product - Prodcut mmm.dd.yyyy.xlsx. In this way, the Product files can be stored in an easy to lookup structure.
Sub CreateMonthlyReports()
Dim cell As Range
Dim dict As Object, vKey As Variant
Dim Key As String
Dim SheetsInNewWorkbook As Long
Dim DateOf As Date
DateOf = DateSerial(Year(Date), Month(Date), 1)
With Application
.ScreenUpdating = False
SheetsInNewWorkbook = .SheetsInNewWorkbook
.SheetsInNewWorkbook = 1
End With
Set dict = CreateObject("Scripting.Dictionary")
With ThisWorkbook.Worksheets("List")
For Each cell In .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
Key = Left(cell.Value, 4)
'Store an ArrayList in the Scripting.Dictionary that can be retrieved using the Product Key
If Not dict.exists(Key) Then dict.Add Key, CreateObject("System.Collections.ArrayList")
Next
End With
With Workbooks("Master.xlsx").Worksheets("Products")
For Each cell In .Range("H2", .Range("H" & .Rows.Count).End(xlUp))
Key = Left(cell.Value, 4)
'Add the Products to the ArrayList in the Scripting.Dictionary that is associated with the Product Key
If dict.exists(Key) Then dict(Key).Add cell.Value
Next
End With
For Each vKey In dict
If dict(vKey).Count > 0 Then
With Workbooks.Add
With .Worksheets(1)
.Name = "Products"
.Range("A1").Value = "Products"
.Range("A2").Resize(dict(vKey).Count).Value = Application.Transpose(dict(vKey).ToArray)
End With
.SaveAs FileName:=getMonthlyFileName(DateOf, CStr(vKey)), FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
.Close SaveChanges:=False
End With
End If
Next
With Application
.ScreenUpdating = True
.SheetsInNewWorkbook = SheetsInNewWorkbook
End With
End Sub
Function getMonthlyFileName(DateOf As Date, Product As String) As String
Dim path As String
path = ThisWorkbook.path & "\Product Reports\"
If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
path = path & Format(DateOf, "yyyy") & "\"
If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
path = path & Format(DateOf, "mmm") & "\"
If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
getMonthlyFileName = path & "Product - " & Product & Format(DateOf, " mmm.dd.yyyy") & ".xlsx"
End Function
Try two loops for this, making sure you sort by the product in the main list to make this a little quicker.
Dim i as Long, j as Long, k as Long, l as Long, m as Long
With Sheets("List")
j = .Cells( .Rows.Count, 1).End(xlUp).Row
End With
With Sheets("Products")
l = .Cells( .Rows.Count, 1).End(xlUp).Row
End With
For i = 2 to j
For k = 2 to l
If Sheets("List").Cells(i,1).Value = Sheets("Products").Cells(k,1).Value Then
With Sheets("Output")
m = .Cells( .Rows.Count, 1).End(xlUp).Row
End With
Sheets("Output").Rows(m+1).Value = Sheets("Products").Rows(k).Value
End If
Next k
Next i
Edit
Will try to piecemeal something to give at least a lead to splitting into different sheets, rather than having one output sheet (this will not be tested, just free-coding):
Dim i as Long, j as Long, k as Long, l as Long, m as Long, n as String
With Sheets("List")
j = .Cells( .Rows.Count, 1).End(xlUp).Row
End With
With Sheets("Products")
l = .Cells( .Rows.Count, 1).End(xlUp).Row
End With
For i = 2 to j
n = Sheets("List").Cells(i,1).Value
Sheets.Add(After:=Sheets(Sheets.Count)).Name = n
Sheets(n).Cells(1,1).Value = n
Sheets(n).Rows(2).Value = Sheets("Products").Rows(1).Value
For k = 2 to l
With Sheets(n)
If .Cells(1,1).Value = Sheets("Products").Cells(k,1).Value Then
m = .Cells( .Rows.Count, 1).End(xlUp).Row
.Rows(m+1).Value = Sheets("Products").Rows(k).Value
End If
Next k
Next i
I don't know why some people doing VBA thinks declaring all the variables with weird names before a thousand lines of code is a good idea.........
Anyways..back to the question, I believe what you are trying to achieve is:
1) Specify a list whilst the code iterates through the list and filters the data based on the listed items.
2) Creates a workbook where the filtered the data is copied over.
3) saving the workbook to somewhere you'll specify, with a specific name.
So naturally, your programme access point should be the one that iterates through the specified list, which should be your main function.
Then inside main function you'll have a Sub that deals with whatever the product ID is, and then filters on your product ID, then copies the data into a newly created workbook.
Last step would be naming the new workbook and saving it close it.
So here is some code skeleton that hopefully will help you with creating the monthly reports. You'll have to write yourself how you want to copy the data from your master workbook to the destination workbook (it should be simple enough, just filter the source list and copy the results to the destination workbook, no dictionary nor arraylist is needed).
Sub main()
Dim rngIdx As Range
Set rngIdx = ThisWorkbook.Sheets("where your list is").Range("A1")
With Application
.DisplayAlerts = False
.ScreenUpdating = False
End With
While (rngIdx.Value <> "")
Call create_report(rngIdx.Value)
Set rngIdx = rngIdx.Offset(1, 0)
Wend
With Application
.DisplayAlerts = True
.ScreenUpdating = True
End With
End Sub
Sub create_report(ByVal product_ID As String)
Dim dest_wbk As Workbook
Set dest_wbk = Workbooks.Add
Call do_whatever(ThisWorkbook, dest_wbk, product_ID)
dest_wbk.SaveAs getMonthlyFileName(some_date, product_ID)
dest_wbk.Close
End Sub
Sub do_whatever(source_wbk As Workbook, dest_wbk As Workbook, ByVal product_ID As String)
' this is the code where you copy from your master data to the destination workbook
' modify sheet names, formatting.......etc.
End Sub
I am totally new to VBA, looking for tips or hints to solve this question.
I am trying to loop through all the files in a folder and trying to split the filename into three parts that are separated by underscore and then paste those into a spreadsheet. After that, pivot it and count how many files there are in a new sheet.
For example, Filename : CA_File_20170810.txt
So it looks like this:
**IPA TYPE DATE Filename Filepath**
CA File 20170810
*IPA, Type, Date,filename, filepath are columns headers in excel.
Here is what I have in my code so far
Sub LoopingThroughFiles()
Dim f As String
Dim G As String
Dim File As Variant
Dim MyObj As Object
Dim MySource As Object
Dim FileName As Variant
Dim TypeName As Variant
Cells(1, 1) = "IPA"
Cells(1, 2) = "TYPE"
Cells(1, 3) = "DATE"
Cells(1, 4) = "FILENAME"
Cells(1, 5) = "FILEPATH"
Cells(2, 1).Select
f = Dir("C:\Users\kxc8574\Documents\VBA_Practice\")
G = Dir("C:\Users\kxc8574\Documents\VBA_Practice\")
If Right(f, 1) <> "\" Then
f = f + "\"
Cells(2, 1).Select
Do While Len(f) > 0
IpaName = Left(f, InStr(f, "_") - 1)
ActiveCell.Formula = IpaName
ActiveCell.Offset(1, 0).Select
f = Dir()
Loop
Do While Len(G) > 0
TypeName = Mid(G, InStr(G, "_") + 1, InStr(G, "File_") - InStr(G, "_") - 1)
ActiveCell.Formula = TypeName
ActiveCell.Offset(1, 0).Select
G = Dir()
Loop
End If
End Sub
I am missing a lot of things, not sure how to really continue. This code gives me an error "invalid procedure call" when it reaches the G = Dir()
Thanks for your help !!!
First, paste the text under "Explanation" into A1 of a worksheet. Then paste the code under "Code" into a module. Make sure the workbook is in the same directory as your .txt files. Then, run the macro. See animated gif for the result.
"Explanation"
This workbook contains a macro which will
1) Make a new sheet in this workbook named "Combined"
2) Open a copy of each .txt file located in the same directory as this workbook
3) extract the text between "_" characters
4) place the separated text into columns
5) count the number of .txt files processed
Note: Any sheet named "Combined" in this Workbook will be deleted
"Code"
Option Explicit
Sub CombineFiles()
Dim theDir As String, theFile As String
Dim sh As Worksheet, wk As Workbook, newSheet As Worksheet
Dim r As Range, parts() As String
Dim i As Long, s As String
Dim Done As Boolean, numFiles As Integer
Const ext = ".txt"
Err.Clear
theDir = ThisWorkbook.Path
'explain what program does
Worksheets("Program").Select
For i = 1 To 7
s = s & Cells(i, 1) & vbCr & vbCr
Next i
s = s & vbCr
s = MsgBox(s, vbYesNoCancel, "What this macro does")
If s <> vbYes Then End
For Each sh In Worksheets
If sh.Name = "Combined" Then
Application.DisplayAlerts = False
sh.Delete
Application.DisplayAlerts = True
End If
Next
Set newSheet = ThisWorkbook.Sheets.Add
newSheet.Name = "Combined"
'Loop through all files in directory with ext
s = Dir(theDir & "\*" & ext)
Set r = Range("A1")
r = "IPA"
r.Offset(0, 1) = "Type"
r.Offset(0, 2) = "Date"
r.Offset(0, 3) = "filename"
r.Offset(0, 4) = "filepath"
While s <> ""
numFiles = numFiles + 1
parts = Split(s, "_")
Set r = r.Offset(1, 0)
For i = 0 To 2
r.Offset(, i) = Replace(parts(i), ".txt", "")
Next i
r.Offset(, 3) = s
r.Offset(, 4) = theDir & "\" & s & ext
s = Dir()
Wend
MsgBox (numFiles & " files were processed.")
End Sub
Untested but should give you some idea:
Sub LoopingThroughFiles()
Const FPATH As String = "C:\Users\kxc8574\Documents\VBA_Practice\"
Dim f As String, i As Long, arr, sht As Worksheet
Set sht = ActiveSheet
sht.Cells(1, 1).Resize(1, 5).Value = _
Array("IPA", "TYPE", "DATE", "FILENAME", "FILEPATH")
f = Dir(FPATH & "*.txt") '<< only txt files
i = 2
Do While f <> ""
'split filename on underscore after replacing the ".txt"
arr = Split(Replace(f, ".txt", ""), "_", 3)
sht.Cells(i, 1).Resize(1, UBound(arr) + 1).Value = arr
sht.Cells(i, 4).Value = f
sht.Cells(i, 5).Value = FPATH
f = Dir() '<< next file
i = i + 1
Loop
End Sub
Untested but perhaps something like this??
Sub HashFiles()
Dim MyDir As String, MyIPA As Variant, MyType As Variant, MyDate As Variant, i As Integer, oFile As Object, oFSO As Object, oFolder As Object, oFiles As Object
MyDir = "C:\Users\kxc8574\Documents\VBA_Practice\"
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFolder = oFSO.GetFolder(MyDir)
Set oFiles = oFolder.Files
ReDim MyIPA(1 To oFiles.Count)
ReDim MyType(1 To oFiles.Count)
ReDim MyDate(1 To oFiles.Count)
i = 1
For Each oFile In oFiles
MyIPA(i) = Split(oFile.Name, "_")(0)
MyType(i) = Split(oFile.Name, "_")(1)
MyDate(i) = Split(oFile.Name, "_")(2)
i = i + 1
Next
Range("A2").Resize(UBound(MyIPA) + 1, 1) = Application.Transpose(MyIPA)
Range("B2").Resize(UBound(MyType) + 1, 1) = Application.Transpose(MyType)
Range("C2").Resize(UBound(MyDate) + 1, 1) = Application.Transpose(MyDate)
End Sub
I have written a Macro that opens the selected file, searches where the error is occurred and then places it in the Summary file in the active cell. Its working perfectly but now I want that instead of selecting each file one by one i could select all required files together or i could open one file and it runs till the last file as the file name is in series i.e Motor 21, motor 22 and so on. there is some help give in this post but I don't know if I could use it.
http://www.mrexcel.com/forum/excel-questions/602222-macro-compile-data-multiple-excel-files-into-one-summary-file.html
Sub InputData()
Dim fNameAndPath As Variant
Dim wb As Workbook, temporaryWB As Workbook
Dim oRange As Range, aCell As Range, bCell As Range
Dim ws As Worksheet
Dim SearchString As String, DateCol As String
Dim CumSum As Double, counter As Double, cum As Double
Dim strSheetName As String, CellName As String
Dim lastColumn As Long
Set wb = ThisWorkbook
strSheetName = ActiveSheet.Name
CellName = ActiveCell.Address
cum = Range(CellName).Offset(-1, 2).Value
fNameAndPath = Application.GetOpenFilename(Title:="Select File To Be Opened")
If fNameAndPath = False Then Exit Sub
Set temporaryWB = Workbooks.Open(fNameAndPath)
Set ws = ActiveSheet
Set oRange = ws.Range("C:C")
SearchString = "10000"
Set aCell = oRange.Find(What:=SearchString, LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, _
MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then ' searching codeID string first time
aCell.Select
DateCol = aCell.Offset(0, -2)
counter = aCell.Offset(0, -1)
wb.Worksheets(strSheetName).Range(CellName) = DateCol
wb.Worksheets(strSheetName).Range(CellName).Offset(0, 1) = counter
CumSum = counter + cum
wb.Worksheets(strSheetName).Range(CellName).Offset(0, 2) = CumSum
wb.Worksheets(strSheetName).Range(CellName).Offset(0, 3) = "1000000"
wb.Worksheets(strSheetName).Range(CellName).Offset(0, 4) = "50"
lastColumn = ws.UsedRange.Columns.Count
If InStr(1, ActiveCell.End(xlToRight).Offset(1, 3).Value, "1ms", vbTextCompare) <> 0 Then
wb.Worksheets(strSheetName).Range(CellName).Offset(0, 6) = ActiveCell.End(xlToRight).Offset(1, 3)
wb.Worksheets(strSheetName).Range(CellName).Offset(0, 7) = ActiveCell.End(xlToRight).Offset(1, 4)
Else
wb.Worksheets(strSheetName).Range(CellName).Offset(0, 6) = Application.InputBox("Enter error", "Dialog box", ActiveCell.End(xlToRight).Offset(1, 3), , , , , 2)
wb.Worksheets(strSheetName).Range(CellName).Offset(0, 7) = Application.InputBox("Enter error", "Dialog box", ActiveCell.End(xlToRight).Offset(1, 4), , , , , 2)
End If
Else
MsgBox SearchString & " not Found"
Exit Sub
End If
temporaryWB.Close savechanges:=False
End Sub
The Application.GetOpenFileName method has an optional multiselect parameter.
Working with a returned value that could be either False or an array of filenames and paths (even if that array is an array of one filename/path) is a bit tricky. Here is some framework that should get you started.
Sub collect_fns()
Dim f As Long, fNameAndPath As Variant
fNameAndPath = Application.GetOpenFilename("Excel files (*.xl*), *.xl*", _
Title:="Select File(s) To Be Opened", MultiSelect:=True)
If IsArray(fNameAndPath) Then
For f = LBound(fNameAndPath) To UBound(fNameAndPath)
' do something with each file as fNameAndPath(f)
process_each_fn CStr(fNameAndPath(f))
Next f
Else
'no files selected
End If
End Sub
Sub process_each_fn(fn As String)
Debug.Print fn
End Sub
It may help to move the bulk of your code to another sub and pass the filename into the new sub through the loop as I'm demonstrated above.