Looping vlookup through predefined named range in multiple sheets - excel

So I've been solving this problem of mine for a couple days already.
Basically, I have multiple green sheets (my source sheets) and one main sheet (master sheet), the problem I'm working on has to do with looping through these green sheets in order to pull certain information from them and put it on certain columns in my master sheet.
Here's the layout of one of these green sheets for better understanding:
https://imgur.com/cayZXUA
I'm sorry for the links, cant add images yet
You can see that these green sheets consist of multiple boxes which can differ in size from sheet to sheet. Some of the values I need to retrieve are fixed in the same cell address for all green sheets so I have no problem getting them to the master sheet. But there are some cases like this:
https://imgur.com/nPYyLbM
Assumption box contains information that I need to lookup and pull it to Main sheet. In essence, this box can take up vertically any space so that address for values of payroll, tax and miscellaneous expenditures changes.
I came up with the idea of giving these boxes in all green sheets name "Assumptions" like seen in the image above. So the questions is how do I lookup 3rd column of this named box and pull it to main sheet?
Here's Main sheet structure:
https://imgur.com/CWMpGvH
My code so far:
Sub CombiningSheets()
Dim p_value, cst_value, m_value As Long
Dim p, cst, m As String
p = "payroll"
cst = "consolidated social tax"
m = "miscellaneous expenditures"
With ThisWorkbook.Sheets("Main")
For Each wsheet In ThisWorkbook.Sheets
If wsheet.Name <> "Main" Then
Set nextEntry = .Cells(.Rows.Count, "G").End(xlUp).Offset(1, 0)
Set nextEntry_FTE_quantity = .Cells(.Rows.Count, "K").End(xlUp).Offset(1, 0)
Set nextEntry_nonrecurring_expenses = .Cells(.Rows.Count, "S").End(xlUp).Offset(1, 0)
Set nextEntry_initiative_type = .Cells(.Rows.Count, "Q").End(xlUp).Offset(1, 0)
Set nextEntry_initiative_description = .Cells(.Rows.Count, "E").End(xlUp).Offset(1, 0)
Set nextEntry_economic_benefit = .Cells(.Rows.Count, "AA").End(xlUp).Offset(1, 0)
Set nextEntry_payroll = .Cells(.Rows.Count, "AI").End(xlUp).Offset(1, 0)
Set nextEntry_consolidated_social_tax = .Cells(.Rows.Count, "AJ").End(xlUp).Offset(1, 0)
Set nextEntry_miscellaneous_expenditures = .Cells(.Rows.Count, "AK").End(xlUp).Offset(1, 0)
If IsError(Application.Match(wsheet.Name, .Range("G:G"), 0)) Then
nextEntry.Value = wsheet.Name
nextEntry_initiative_description.Value = wsheet.Range("K6").Value
nextEntry_FTE_quantity.Value = wsheet.Range("BH16").Value
nextEntry_initiative_type.Value = wsheet.Range("K8").Value
nextEntry_nonrecurring_expenses.Value = wsheet.Range("BH17").Value
nextEntry_economic_benefit.Value = wsheet.Range("BH15").Value
End If
End If
Debug.Print wsheet.Name
Next wsheet
End With
End Sub

From your questions it seems that you have defined named ranges. As I'm aware of your question How to copy sheets with certain tab color from one workbook to another? I do believe that you don't have named ranges on your individual sheets.
Below you find some code if you have named ranges (Sub List_NamedRange_Loop).If you don't have named ranges you can create these named ranges on the individual sheets first (Sub Create_NamedRange).
At the end of this post you find a screenshot of the result I got.
Sub List_NamedRange_Loop()
Dim NamedRange As Name
Dim ws As Worksheet
Dim PrDebug As Boolean
Dim iCt As Integer
PrDebug = False ' => Output to Worksheet "Main"
'PrDebug = True ' => Output to Immediate Window (Ctrl-G in VBE)
'List on sheet "main"
If Not (PrDebug) Then
On Error Resume Next
Debug.Print ActiveWorkbook.Name
Sheets("main").Activate
If ActiveSheet.Name <> "main" Then
Worksheets.Add
ActiveSheet.Name = "main"
End If
On Error GoTo 0
Range("A1:D1000").ClearContents
Range("A1").Value = "Sheet Name"
Range("B1").Value = "Named Range"
Range("C1").Value = "RefersTo"
Range("D1").Value = "Value (Direct Reference)"
Range("E1").Value = "Value (Named Reference)"
End If
'We expect all named ranges to be local = defined on the indivdual sheets
'so no need for the below 'workbook loop'
'Loop through each named range in workbook
' For Each namedrange In ActiveWorkbook.Names
' Debug.Print namedrange.Name, namedrange.RefersTo
' Next namedrange
'Loop through each named range scoped to a specific worksheet
iCt = 0
For Each ws In Worksheets
iCt = iCt + 1
If ws.Names.Count > 0 Then
If PrDebug Then
Debug.Print
Debug.Print ws.Name
Else
End If
For Each NamedRange In ws.Names 'Worksheets("Sheet1").Names
If PrDebug Then
Debug.Print ws.Name, NamedRange.Name, NamedRange.RefersTo
Else
iCt = iCt + 1
Range("A1").Offset(iCt, 0).Value = ws.Name
' Range("B1").Offset(iCt, 0).Value = Replace(NamedRange.Name, ws.Name & "!", "")
If InStr(1, NamedRange.Name, "'") Then
Range("B1").Offset(iCt, 0).Value = Replace(NamedRange.Name, "'" & ws.Name & "'!", "")
Else
Range("B1").Offset(iCt, 0).Value = Replace(NamedRange.Name, ws.Name & "!", "")
End If
Range("C1").Offset(iCt, 0).Value = "'" & NamedRange.RefersTo
Range("D1").Offset(iCt, 0).Value = NamedRange.RefersTo
Range("E1").Offset(iCt, 0).Formula = "=" & NamedRange.Name
Range("E1").Offset(iCt, 0).Calculate
End If
Next NamedRange
Else
' iCt = iCt + 1
' Range("A1").Offset(iCt, 0).Value = ws.Name
' Range("B1").Offset(iCt, 0).Value = "NO NAMES DEFINED!"
End If
Next ws
End Sub
If you don't have named ranges you might create them with the code similar to the following:
Sub Create_NamedRange()
Dim ws As Worksheet
Dim foundRange As Range
For Each ws In Worksheets
If ws.Name <> "main" Then
Debug.Print ws.Name
Set foundRange = ws.Cells.Find(What:="payroll", After:=ActiveCell, LookIn:=xlValues, LookAt _
:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
True, SearchFormat:=False)
If Not (foundRange Is Nothing) Then
Debug.Print "Found: "; ws.Name
'offset between AR and BH: 16 columns (https://imgur.com/nPYyLbM)
ws.Names.Add Name:="payroll", RefersTo:=foundRange.Offset(0, 16)
ws.Names.Add Name:="consolidated_social_tax", RefersTo:=foundRange.Offset(1, 16)
ws.Names.Add Name:="miscellaneous_expenditures", RefersTo:=foundRange.Offset(2, 16)
End If
End If
Next ws
End Sub

I would use Range.Find to locate the cells by keywords and return the values adjacent to them.
Sub TestFind()
Dim colOffset As Long
Dim wsheet As Worksheet
colOffset = Columns("BH").Column - Columns("AR").Column - 2 'Two Extra Cells in Merged Range Adjustment
For Each wsheet In ThisWorkbook.Worksheets
If wsheet.Name <> "Main" Then
Debug.Print FindValueRelativeToSearch(wsheet.Columns("AR"), "payroll", 0, colOffset)
Debug.Print FindValueRelativeToSearch(wsheet.Columns("AR"), "social tax", 0, colOffset)
Debug.Print FindValueRelativeToSearch(wsheet.Columns("AR:AT"), "miscellaneous expenditures", 0, colOffset)
End If
Next
End Sub
Function FindValueRelativeToSearch(SearchRange As Range, search As String, rowOffset As Long, colOffset As Long) As Variant
Dim cell As Range
Application.FindFormat.MergeCells = True
With SearchRange
Set cell = .Find(What:=search, After:=.Cells(1, 1), _
LookIn:=xlFormulas, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=True)
End With
cell.Offset(rowOffset, colOffset).Activate
If cell Is Nothing Then
Debug.Print "Search not found: FindValueRelativeToSearch()", SearchRange.Address(0, 0, xlA1, True), search
Else
FindValueRelativeToSearch = cell.Offset(rowOffset, colOffset).Value
End If
End Function

Related

Locating all the tables on one spreadsheet using Excel VBA

I have a few spreadsheets with various tables in different formats. My task is to locate and identify anything on the spreadsheets that can be considered a table, and flatten it into a text file. Currently I am only looking for a solution to locate all tables on one spreadsheet.
The rules are:
Spreadsheet format is somewhat fixed, I have to process what I am given.
A completely empty line can split a table into two, unless there's a sure way to tell what is a missing line within one table and what is an actual new table.
I can handle merged fields beforehand if needs be (split them and backfill with the common value, that's already written and is working)
The tables could have a different number of columns, different header rows, and they could begin in any column.
I consider records in the same line to be part of the same table, I am not expecting to find tables next to one another.
The code I have so far as follows:
Sub Find_All_Tables()
'Finds all the separate tables in the worksheet
Dim rStart As Range, rFoundStart As Range, rFoundEnd As Range
Dim lRow As Long, lCol As Long
lRow = Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
lCol = Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
MsgBox "Last non-empty cell on the spreadsheet is " & Cells(lRow, lCol).Address
Set rStart = Range("A1")
MsgBox rStart.Row
While rStart.Row < lRow
On Error Resume Next
Set rFoundStart = Cells.Find(What:="*", _
After:=rStart, _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
On Error GoTo 0
If rFoundStart Is Nothing Then
MsgBox "All cells are blank."
Else
rFoundStart.End(xlToRight).End(xlDown).Select
Set rFoundEnd = Selection
'MsgBox "First Cell: " & rFoundStart.Address
'MsgBox "Last Cell: " & ActiveCell.Address
Range(rFoundStart.Address, rFoundEnd.Address).Select
MsgBox "There is a table between " & rFoundStart.Address & " and " & rFoundEnd.Address
End If
Set rStart = Range("A" & rFoundEnd.Row + 1)
Wend
End Sub
The sample sheet I am looking at is as messy as possible to account for "creative" formatting.
The error I'm getting is due to the fact that the second table starts from B7 and ends in E1048576, which is well past the loop condition - I would like this range to end in E8 (or E9 if possible or once the merged cells are broken up).
I've got this code from way back when.... 2008.
No idea if it works with ListObject tables.
Original MrExcel post: Find all lists in a workbook
Sub Test()
Dim aLists As Variant
Dim aLists1 As Variant
'//Find lists in a different workbook.
'' aLists = FindRegionsInWorkbook(Workbooks("Test Workbook.xls"))
'//Find lists in the this workbook.
aLists1 = FindRegionsInWorkbook(ThisWorkbook)
Debug.Assert False
End Sub
'//Returns each region in each worksheet within the workbook in the 'sRegion' variable.
'//
'//Written by Zack Barresse (MVP), Oregon, USA.
'//
'//http://www.mrexcel.com/forum/showthread.php?t=309052
Public Function FindRegionsInWorkbook(wrkBk As Workbook) As Variant
Dim ws As Worksheet, rRegion As Range, sRegion As String, sCheck As String
Dim sAddys As String, arrAddys() As String, aRegions() As Variant
Dim iCnt As Long, i As Long, j As Long
'//Cycle through each worksheet in workbook.
j = 0
For Each ws In wrkBk.Worksheets
sAddys = vbNullString
sRegion = vbNullString
On Error Resume Next
'//Find all ranges of constant & formula valies in worksheet.
sAddys = ws.Cells.SpecialCells(xlCellTypeConstants, 23).Address(0, 0) & ","
sAddys = sAddys & ws.Cells.SpecialCells(xlCellTypeFormulas, 23).Address(0, 0)
If Right(sAddys, 1) = "," Then sAddys = Left(sAddys, Len(sAddys) - 1)
On Error GoTo 0
If sAddys = vbNullString Then GoTo SkipWs
'//Put each seperate range into an array.
If InStr(1, sAddys, ",") = 0 Then
ReDim arrAddys(1 To 1, 1 To 2)
arrAddys(1, 1) = ws.Name
arrAddys(1, 2) = sAddys
Else
arrAddys = Split(sAddys, ",")
For i = LBound(arrAddys) To UBound(arrAddys)
arrAddys(i) = "'" & ws.Name & "'!" & arrAddys(i)
Next i
End If
'//Place region that range sits in into sRegion (if not already in there).
For i = LBound(arrAddys) To UBound(arrAddys)
If InStr(1, sRegion, ws.Range(arrAddys(i)).CurrentRegion.Address(0, 0)) = 0 Then
sRegion = sRegion & ws.Range(arrAddys(i)).CurrentRegion.Address(0, 0) & "," '*** no sheet
sCheck = Right(arrAddys(i), Len(arrAddys(i)) - InStr(1, arrAddys(i), "!"))
ReDim Preserve aRegions(0 To j)
aRegions(j) = Left(arrAddys(i), InStr(1, arrAddys(i), "!") - 1) & "!" & ws.Range(sCheck).CurrentRegion.Address(0, 0)
j = j + 1
End If
Next i
SkipWs:
Next ws
On Error GoTo ErrHandle
FindRegionsInWorkbook = aRegions
Exit Function
ErrHandle:
'things you might want done if no lists were found...
End Function

Excel VBA will not locate date

I am working to create an add on style sheet to my company timesheet that will autofill company paid holidays by just the user inserting the dates. I use formulas on the excel timesheets to autofill the dates for the entire year so that I save time doing my bi-weekly payroll form. I have a holiday sheet that I name the holidays and input the date they are observed. The code is supposed to search all worksheets in the workbook until it finds the date for the corresponding holiday and input the number of hours off, the holiday code and name. The code I have written will find any date I insert up to 11/9/2022 and after this date it will not find any further dates. I have tried many things including changing the date column format, using different criteria settings for the .Find and even removing the formula from the date column and actually writing in 11/11/2022 and it is still unable to locate the date while using .Find. Please any help would be appreciated. I have added a few screens and code snippets of what I have so far.
Sub VeteransDay()
Dim ws As Worksheet
Dim FindString As String
Dim Rng As Range
FindString = Sheets("Holiday").Range("B9").Value
If Trim(FindString) <> "" Then
For Each ws In Worksheets
If ws.Name <> "Holiday" Then
With ws.UsedRange
Set Rng = .Find(What:=FindString, After:=.Cells(1, 1), LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False)
If Not Rng Is Nothing Then
sheetName = ws.Name
Cell_Add = Split(Rng.Address, "$")
ThisCol = Cell_Add(1)
ThisRow = Cell_Add(2)
Worksheets(sheetName).Range("K" & ThisRow).Value = 8
Worksheets(sheetName).Range("K" & ThisRow).Font.Color = vbRed
Worksheets(sheetName).Range("L" & ThisRow).Value = "HD"
Worksheets(sheetName).Range("L" & ThisRow).Font.Color = vbRed
Worksheets(sheetName).Range("M" & ThisRow).Value = Range("A9")
Worksheets(sheetName).Range("M" & ThisRow).Font.Color = vbRed
Exit Sub
End If
End With
End If
Next ws
End If
End Sub
enter image description here
enter image description here
Try this, the search is restricted to the range B1:B37 on each sheet.
Option Explicit
Sub VeteransDay()
Dim ws As Worksheet, ar, r
Dim dt As Date, sName As String, n As Long
Dim arHoliday, lastrow As Long, i As Long
With Sheets("Holiday")
lastrow = .Cells(.Rows.Count, "B").End(xlUp).Row
arHoliday = .Range("A1:B" & lastrow).Value
End With
For Each ws In Worksheets
If ws.Name <> "Holiday" Then
' loop through holidays
For i = 1 To UBound(arHoliday)
dt = arHoliday(i, 2)
r = Application.Match(CDbl(dt), ws.Range("B1:B37").Value2, 0)
If Not IsError(r) Then
'MsgBox ws.Name & " row " & r
With ws.Range("K" & r)
.Value = 8
.Offset(, 1) = "HD"
.Offset(, 2) = arHoliday(i, 1) ' col A
.Resize(, 3).Font.Color = vbRed
n = n + 1
End With
End If
Next
End If
Next ws
MsgBox n & " found for all dates", vbInformation
End Sub

VBA to copy specific cells from one worksheet to another upon meeting a criteria

My VBA knowledge is very limited, so looking for some help here. Tried some Googling and putting together a code but hasn't met the goal. Appreciate the help here!
I have 2 worksheets:
Data - source worksheet with the data to be copied
Dashboard - Target sheet for pasting
Data sheet - It has multiple columns, the ones I have named are the ones I need to be copied except the column named "Sold?" which is for criteria. The other columns with no names in the image actually have data, to avoid confusion I have removed them here.
This sheet grows and I will add a new row of data when needed.
Dashboard Sheet - When I click "Refresh" button, I want the code to check the "Data" sheet and if a row meets of criteria of Sold? = "N", then only data from column C,G,J,M should be copied and pasted into columns B,C,D,E of "Dashboard" sheet. Additional criteria: if an investment name repeats, the details need to be summed up and shown in Dashboard sheet. I have provided my expected output in the image. (ABC & TY summed up)
I have tried a bit but unable to incorporate all the criteria and this code when run doesn't throw an error but does nothing, no output.
Private Sub Refresh_Click()
Worksheets("Dashboard").Activate
Application.ScreenUpdating = True
a = Worksheets("Data").Cells(Rows.Count, "A").End(xlUp).Row
For i = 12 To a
If Worksheets("Data").Cells(i, 15).Value = "N" Then
Worksheets("Data").Cells(i, 3).Copy
Worksheets("Data").Cells(i, 7).Copy
Worksheets("Data").Cells(i, 13).Copy
Worksheets("Data").Cells(i, 14).Copy
Worksheets("Dashboard").Activate
Worksheets("Dashboard").Range("B6:G25").Select
ActiveSheet.Paste
End If
Next
Application.CutCopyMode = False
End Sub
I strongly suggest a pivot table. Still if you want VBA based solution, you might try this code:
Option Explicit
Private Sub Refresh_Click()
'Declarations.
Dim BlnHiddenColumns() As Boolean
Dim DblFirstRow As Double
Dim DblLastRow As Double
Dim DblCounter01 As Double
Dim DblCounterLimit01 As Double
Dim DblInvestmentNameColumn As Double
Dim DblQuantityColumn As Double
Dim DblAfterChargeColumn As Double
Dim DblCurrentPLColumn As Double
Dim DblSoldColumn As Double
Dim RngData As Range
Dim RngResult As Range
Dim StrAutofilterAddress As String
Dim StrMarker As String
Dim StrInvestmentNameHeader As String
Dim StrQuantityHeader As String
Dim StrAfterChargeHeader As String
Dim StrCurrentPLHeader As String
Dim WksData As Worksheet
Dim WksDashboard As Worksheet
Dim WksPivotTable As Worksheet
Dim PvtPivotTable01 As PivotTable
'Settings.
DblInvestmentNameColumn = 3
DblQuantityColumn = 7
DblAfterChargeColumn = 10
DblCurrentPLColumn = 13
DblSoldColumn = 15
DblFirstRow = 12
DblCounterLimit01 = 1000
StrMarker = "N"
Set WksData = Worksheets("Data")
DblLastRow = WksData.Cells(Rows.Count, "B").End(xlUp).Row
Set RngData = WksData.Range(WksData.Cells(DblFirstRow - 1, Excel.WorksheetFunction.Min(DblInvestmentNameColumn, DblQuantityColumn, DblAfterChargeColumn, DblCurrentPLColumn, DblSoldColumn)), WksData.Cells(DblLastRow, Excel.WorksheetFunction.Max(DblInvestmentNameColumn, DblQuantityColumn, DblAfterChargeColumn, DblCurrentPLColumn, DblSoldColumn)))
ReDim BlnHiddenColumns(1 To RngData.Columns.Count)
Set WksDashboard = Worksheets("Dashboard")
Set RngResult = WksDashboard.Range("B6")
StrInvestmentNameHeader = WksDashboard.Range("B5").Value
StrQuantityHeader = WksDashboard.Range("C5").Value
StrAfterChargeHeader = WksDashboard.Range("D5").Value
StrCurrentPLHeader = WksDashboard.Range("E5").Value
'Turning off screen updating.
Application.ScreenUpdating = False
'Checking for any previous results list.
If Excel.WorksheetFunction.CountBlank(RngResult) <> RngResult.Cells.Count Then
DblCounter01 = 0
'Checking each row of the result list until an entirely blank row is found.
Do Until Excel.WorksheetFunction.CountBlank(RngResult.Offset(DblCounter01, 0)) = RngResult.Cells.Count
DblCounter01 = DblCounter01 + 1
'If the number of rows checked is equal or superior to DblCounterLimit01 the macro is terminated.
If DblCounter01 >= DblCounterLimit01 Then
MsgBox "Please clear the current holdings list manually", vbCritical + vbOKOnly, "Unable to clear the current list"
Exit Sub
End If
Loop
'Clearing the list.
RngResult.Parent.Range(RngResult, RngResult.Offset(DblCounter01 - 1)).ClearContents
End If
'Checking for existing autofilter in WksData.
If WksData.AutoFilterMode = True Then
'Coping the address of the autofilter in WksData.
StrAutofilterAddress = WksData.AutoFilter.Range.Address
End If
'Removing any autofilter in WksData.
WksData.AutoFilterMode = False
'Covering each column of RngData.
For DblCounter01 = 1 To RngData.Columns.Count
'Setting BlnHiddenColumns accordingly to the RngData columns' status (hidden/not hidden).
BlnHiddenColumns(DblCounter01) = RngData.Columns(DblCounter01).Hidden
'Hiding the columns of RngData we won't copy.
Select Case DblCounter01 + RngData.Column - 1
Case Is = DblInvestmentNameColumn, DblQuantityColumn, DblAfterChargeColumn, DblCurrentPLColumn
RngData.Columns(DblCounter01).Hidden = False
Case Else
RngData.Columns(DblCounter01).Hidden = True
End Select
Next
'Filtering RngData.
RngData.AutoFilter Field:=DblSoldColumn - RngData.Column + 1, Criteria1:=StrMarker
'Copying the filtered RngData into RngResult.
RngData.Resize(RngData.Rows.Count - 1, RngData.Columns.Count).Offset(1, 0).SpecialCells(xlCellTypeVisible).Copy RngResult
'Restoring the RngData columns to their previous status (hidden/not hidden).
For DblCounter01 = 1 To RngData.Columns.Count
If BlnHiddenColumns(DblCounter01) Then
RngData.Columns(DblCounter01).Hidden = True
Else
RngData.Columns(DblCounter01).Hidden = False
End If
Next
'Removing any autofilter in WksData.
WksData.AutoFilterMode = False
'Restoring any pre-existing autofilter in WksData.
If StrAutofilterAddress <> "" Then
WksData.Range(StrAutofilterAddress).AutoFilter
End If
'Setting RngResult to cover the imported list (headers included).
Set RngResult = RngResult.Offset(-1, 0)
Set RngResult = WksDashboard.Range(RngResult, RngResult.End(xlDown).End(xlToRight))
'Creating WksPivotTable.
Set WksPivotTable = Sheets.Add
'Creating PvtPivotTable01.
Set PvtPivotTable01 = ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, _
SourceData:=RngResult, _
Version:=7 _
).CreatePivotTable(TableDestination:=WksPivotTable.Cells(1, 1), _
TableName:="Temporary Pivot Table", _
DefaultVersion:=7 _
)
'Setting PvtPivotTable01.
With PvtPivotTable01.PivotFields(StrInvestmentNameHeader)
.Orientation = xlRowField
.Position = 1
End With
With PvtPivotTable01
.AddDataField .PivotFields(StrQuantityHeader), "Sum of " & StrQuantityHeader, xlSum
.AddDataField .PivotFields(StrAfterChargeHeader), "Sum of " & StrAfterChargeHeader, xlSum
.AddDataField .PivotFields(StrCurrentPLHeader), "Sum of " & StrCurrentPLHeader, xlSum
.ColumnGrand = False
End With
'Clearing the data from RngResult.
RngResult.Offset(1, 0).Resize(RngResult.Rows.Count - 1).ClearContents
'Copying the PvtPivotTable01 content to RngResult.
PvtPivotTable01.DataBodyRange.Offset(0, -1).Resize(, PvtPivotTable01.DataFields.Count + 1).Copy RngResult.Cells(2, 1)
'Deleting WksPivotTable.
Application.DisplayAlerts = False
WksPivotTable.Delete
Application.DisplayAlerts = True
'Restoring screen updating.
Application.ScreenUpdating = False
End Sub
I've intentionally made it longer than the necessary, especially by creating many variables to avoid hard coded data. This method might be useful in more complex and/or longer codes.

Take non zero values, and adjacent data, from one sheet and create new table in another sheet - VBA loop

I am trying to take the output from a solver model and condense it into a summary report in another sheet. The Solver screen will be lost each time I run it on new data.
My solver screen looks like this
Solver screenshot. The ideal report output will be this table. Notice that January only has two truckloads (TLs) as Solver output (IF(E4:N4=True,Include TL,n/a). So, the new report should skip TLs #3,4,5 (G4:I4) and fill in the table with next valid output (column J). I will always want to associate the unit quantity (E:N) with a product name (D) in the new report.
I am a super novice VBA user. Here is how far I have got in my VBA to accomplish this:
Sub TL_Report()
Dim c As Range
For Each c In ActiveSheet.Range("e5:e30")
If c.Value <> 0 Then
Worksheets("TL_Report").Range("C" & Rows.Count).End(xlUp).Offset(1, 0).Resize(1, 2).Value = Range(c.Offset(0, -1), c).Value
End If
Next c
End Sub
I can figure out how to loop through each column in the solver, but I cannot figure out how to have the new report get reformatted without blanks entries. Any advice on how to write this? Thank you.
According to the data avaiable, i've created this subroutine:
Sub SubReport()
'Declarations.
Dim WksSource As Worksheet
Dim WksReport As Worksheet
Dim WksWorksheet01 As Worksheet
Dim RngMonths As Range
Dim RngTrucks As Range
Dim RngProductList As Range
Dim RngValues As Range
Dim RngTarget As Range
Dim RngRange01 As Range
Dim DblCounter01 As Integer
Dim DblCounter02 As Integer
'Setting WksSource.
Set WksSource = Sheets("TL_Solver")
'Referring to WksSource.
With WksSource
'Setting RngMonths.
Set RngRange01 = .Range("E2")
DblCounter01 = Excel.WorksheetFunction.Min(RngRange01.End(xlToRight).Column, _
.Cells(RngRange01.Row, .Columns.Count).End(xlToLeft).Column _
)
Set RngMonths = .Range( _
RngRange01, _
.Cells(RngRange01.Row, DblCounter01) _
)
'Setting RngTrucks.
Set RngRange01 = .Range("E3")
DblCounter01 = Excel.WorksheetFunction.Min(RngRange01.End(xlToRight).Column, _
.Cells(RngRange01.Row, .Columns.Count).End(xlToLeft).Column _
)
Set RngTrucks = .Range( _
RngRange01, _
.Cells(RngRange01.Row, DblCounter01) _
)
'Setting RngProductList.
Set RngRange01 = RngTrucks.Resize(1, 1).Offset(2, -1)
DblCounter01 = Excel.WorksheetFunction.Min(RngRange01.End(xlDown).Row, _
.Cells(.Rows.Count, RngRange01.Column).End(xlUp).Row _
)
Set RngProductList = .Range( _
RngRange01, _
.Cells(DblCounter01, RngRange01.Column) _
)
'Setting RngValues.
Set RngRange01 = .Cells(RngProductList.Row, RngTrucks.Column)
Set RngValues = RngRange01.Resize(RngProductList.Rows.Count, RngTrucks.Columns.Count)
End With
'Creating a new worksheet for the report.
Set WksReport = ActiveWorkbook.Sheets.Add(After:=WksSource)
'Counting other existing reports if any.
DblCounter01 = 0
For Each WksWorksheet01 In WksReport.Parent.Worksheets()
If Left(WksWorksheet01.Name, 7) = "Report " Then
DblCounter01 = DblCounter01 + 1
End If
Next
'Renaming the current report.
DblCounter02 = DblCounter01
On Error Resume Next
Do Until WksReport.Name = "Report " & DblCounter01
DblCounter01 = DblCounter01 + 1
WksReport.Name = "Report " & DblCounter01
If DblCounter01 - DblCounter02 > 1000 Then GoTo CP_FAILED_RENAMING
Loop
CP_FAILED_RENAMING:
On Error GoTo 0
'Setting RngTarget.
Set RngTarget = WksReport.Range("A1")
'Covering each column in RngValues.
For DblCounter01 = 1 To RngValues.Columns.Count
'Checking if there is any value to report.
If Excel.WorksheetFunction.Sum(RngValues.Columns(DblCounter01).Cells) <> 0 Then
'Inserting the data for the first row of the report's chapter.
With RngTarget
.Offset(0, 1).Value = "Truck #"
.Offset(0, 2).Value = Split(RngTrucks.Cells(1, DblCounter01), "#")(1)
.Offset(0, 3).Value = "Delivery"
If WksSource.Cells(RngMonths.Row, RngTrucks.Columns(DblCounter01).Column).Value = "" Then
.Offset(0, 4).Value = WksSource.Cells(RngMonths.Row, RngTrucks.Columns(DblCounter01).Column).End(xlToLeft).Value
Else
.Offset(0, 4).Value = WksSource.Cells(RngMonths.Row, RngTrucks.Columns(DblCounter01).Column).Value
End If
.Offset(1, 1).Value = "Product"
.Offset(1, 2).Value = "Quantity"
End With
'Offsetting RngTarget by 2 rows in order to enter the data.
Set RngTarget = RngTarget.Offset(2, 0)
'Covering each value in the given column of RngValues.
DblCounter02 = 1
For Each RngRange01 In RngValues.Columns(DblCounter01).Cells
'Checking if the value is not 0.
If RngRange01.Value <> 0 Then
'Inserting the data.
With RngTarget
.Value = DblCounter02
.Offset(0, 1).Value = WksSource.Cells(RngRange01.Row, RngProductList.Column).Value
.Offset(0, 2).Value = RngRange01.Value
End With
DblCounter02 = DblCounter02 + 1
'Offsetting RngTarget to the next row of the report.
Set RngTarget = RngTarget.Offset(1, 0)
End If
Next
'Offsetting RngTarget by 1 row for the next chapter.
Set RngTarget = RngTarget.Offset(1, 0)
End If
Next
'Autofitting the second column of the report.
RngTarget.Offset(0, 1).EntireColumn.AutoFit
End Sub
It dynamically determines the size of the data to process (starting from given cells), it creates a new sheet renamed as "Report n" (based of the n pre-existing sheet already named "Report n") and insert the data as requested.

Use stored value to call or create & call sheet

I have a workbook that creates other workbooks and shifts data to them based on the value in column one. Afterwords I need the workbook to store the data it has just copied in a sheet of the same name as the stored variable (in the next empty row), or create the tab if it does not exist.
However i'm having an issue pasting into the tab with the name of the variable, and no idea how to create a new sheet if the variable does not already exist as a sheet.
It's the With Worksheets(SupID).Range("A" & LastRow(SupID) + 1).Paste i'm having trouble with.
Current code below. Thanks!
Private Sub CopyItOver()
Dim myVal As String
Dim SupID As String
'Store Supplier ID
SupID = Trim(Sheets("Raw Data").Range("A2").Value)
'Create workbook
Set newbook = Workbooks.Add
'Copy Records
Set myRng = Workbooks("Book1.xlsm").Worksheets("Raw Data").Range("B2:X7")
myRng.Copy
newbook.Worksheets("Sheet1").Range("A2").PasteSpecial (xlPasteValues)
'Create Header
newbook.Worksheets("Sheet1").Range("A1").Value = "ZHF"
newbook.Worksheets("Sheet1").Range("B1").Value = "CTO"
newbook.Worksheets("Sheet1").Range("C1").Value = "RET"
newbook.Worksheets("Sheet1").Range("D1").Value = SupID
newbook.Worksheets("Sheet1").Range("E1").Value = "RET"
newbook.Worksheets("Sheet1").Range("F1").Value = "RET"
newbook.Worksheets("Sheet1").Range("G1").Value = "6"
newbook.Worksheets("Sheet1").Range("H1").Value = "PROD"
newbook.Worksheets("Sheet1").Range("J1").Value = newbook.Worksheets("Sheet1").Range("B1").Value _
& newbook.Worksheets("Sheet1").Range("D1").Value & "TEMPNUMBER"
newbook.Worksheets("Sheet1").Range("I1").Value = newbook.Worksheets("Sheet1").Range("J1").Value _
& newbook.Worksheets("Sheet1").Range("C1").Value & ".CSV"
newbook.Worksheets("Sheet1").Range("K1") = Format(Date, "ddmmyyyy")
newbook.Worksheets("Sheet1").Range("L1").Value = "Unknown"
newbook.Worksheets("Sheet1").Range("M1").Value = "1"
LastRow = newbook.Worksheets("Sheet1").Cells(Rows.Count, "A").End(xlUp).Row
'Create Footer
newbook.Worksheets("Sheet1").Range("A" & LastRow + 1).Value = "ZFV"
newbook.Worksheets("Sheet1").Range("B" & LastRow + 1).Value = "BATCH" & "TEMPNUMBER"
newbook.Worksheets("Sheet1").Range("C" & LastRow + 1).Value = WorksheetFunction.CountIf(Sheets("Sheet1").Range("A1:A1000"), "RET")
'Name Sheet
myVal = newbook.Worksheets("Sheet1").Range("J1").Value & "RET"
newbook.Worksheets("Sheet1").Name = myVal
'Copy to relevant matching sheet
With Worksheets(SupID).Range("A" & LastRow(SupID) + 1).Paste
End With
'Save Workbook
NewBook.SaveAs Filename:=NewBook.Worksheets("Sheet1").Range("I1").Value
End Sub
Function DLastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlValues, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
End Function
The error's occurring because Worksheets(SupID).Range("A" & LastRow(SupID) + 1).Paste is trying to find that worksheet on your active book, ie the new book. You'd need either to Activate your raw data workbook or change the line to ThisWorkbook.Worksheets(SupID).Range("A" & LastRow(SupID) + 1).Paste.
However, it's not great to use (either explicitly or implicitly) Activate, Select or other keystroke style commands in VBA. Given that you're only copying values (and not the worksheet formats) then, you'd probably be better served reading the data into an array of variants and manipulating those. I've adjusted your code to demonstrate this.
There are some other coding aspects that might not be as robust as they could be. I won't list them all but a comparison of this code with yours will help you see them.
Private Sub CopyItOver()
Dim newBook As Workbook
Dim supSheet As Worksheet
Dim v As Variant
Dim supID As String
Dim namePrefix As String
Dim footerCount As Integer
Dim i As Integer
'Store Supplier ID
supID = Trim(ThisWorkbook.Worksheets("Raw Data").Range("A2").value)
namePrefix = "CTO" & supID & "TEMPNUMBER"
'Create workbook
Set newBook = Workbooks.Add
'Copy Records
v = rawDataSheet.Range("B2:X7").value
For i = 1 To UBound(v, 1)
If v(i, 1) = "RET" Then footerCount = footerCount + 1
Next
'Write new sheet
With newBook.Worksheets(1)
'Values
.Range("A2").Resize(UBound(v, 1), UBound(v, 2)).value = v
'Header
.Range("A1").Resize(, 13) = Array( _
"ZHF", "CTO", "RET", supID, "RET", "RET", "6", "PROD", _
namePrefix & "RET.CSV", namePrefix, _
Format(Date, "ddmmyyyy"), "Unknown", "1")
'Footer
.Cells(.Rows.Count, "A").End(xlUp).Offset(1).Resize(, 3).value = Array( _
"ZFV", "BATCH TEMPNUMBER", footerCount)
'Name
.Name = namePrefix & "RET"
'Save
.SaveAs Filename:=namePrefix & "RET.CSV"
End With
'Copy to relevant matching sheet
On Error Resume Next
Set supSheet = ThisWorkbook.Worksheets(supID)
On Error Goto 0
If newSheet Is Nothing Then
With ThisWorkbook.Worksheets
Set supSheet = .Add(After:=.Item(.Count))
End With
supSheet.Name = supID
End If
With supSheet
.Cells(.Rows.Count, "A").End(xlUp).Offset(1).Resize(UBound(v, 1), UBound(v, 2)).value = v
End With
End Sub
A few things that aren't quite right:
Add Option Explicit at the top of the module and declare your variables.
LastRow will be a Long data type, but you're trying to use it like an array in With Worksheets(SupID).Range("A" & LastRow(SupID) + 1).Paste. Just use LastRow+1.
With Worksheets(SupID).Range("A" & LastRow(SupID) + 1).Paste
End With should probably be Worksheets(SupID).Range("A" & LastRow + 1).Paste, but it will paste myRng - can't see anything else you've copied.
At the start of the code you reference Workbooks("Book1.xlsm"). If this is the workbook that the code is in I'd change it to ThisWorkbook.
SupID looks at Raw Data on whichever workbook is active at the time (you'd don't specify the workbook when initialising that variable).
This function will return TRUE/FALSE if a named worksheet exists:
Public Function WorkSheetExists(SheetName As String) As Boolean
Dim wrkSht As Worksheet
On Error Resume Next
Set wrkSht = ThisWorkbook.Worksheets(SheetName)
WorkSheetExists = (Err.Number = 0)
Set wrkSht = Nothing
On Error GoTo 0
End Function
Hope that points you in the right direction :)
Edit:
Just noticed to....
Rather than write:
newbook.Worksheets("Sheet1").Range("A1").Value = "ZHF"
newbook.Worksheets("Sheet1").Range("B1").Value = "CTO"
newbook.Worksheets("Sheet1").Range("C1").Value = "RET"
newbook.Worksheets("Sheet1").Range("D1").Value = SupID
newbook.Worksheets("Sheet1").Range("E1").Value = "RET"
newbook.Worksheets("Sheet1").Range("F1").Value = "RET"
newbook.Worksheets("Sheet1").Range("G1").Value = "6"
newbook.Worksheets("Sheet1").Range("H1").Value = "PROD"
You can just use:
newbook.Worksheets("Sheet1").Range("A1:H1") = Array("ZHF", "CTO", "RET", "SupID", "RET", "RET", "6", "Prod")
I managed to resolve my issue using help from Here, to which I adapted to the code below and ran in a separate module, which allows for the use of a previously unspecified sheet name, that is later derived from a cell value. If the sheet does not exist, it is created matching the name to the stored value and the data pasted into it. Thanks for the support!
Sub TEST()
Dim i As Integer, blnFound As Boolean
blnFound = False
SupID = Trim(Sheets("Raw Data").Range("A2").Value)
Set myRng = Workbooks("Book1.xlsm").Worksheets("Raw Data").Range("B2:X7")
myRng.Copy
With ThisWorkbook
For i = 1 To .Sheets.Count
If .Sheets(i).Name = SupID Then
blnFound = True
.Sheets(i).Activate
ActiveSheet.Paste Destination:=Range("A" & LastRow + 1)
Exit For
End If
Next i
If blnFound = False Then
.Sheets.Add
With ActiveSheet
.Name = SupID
ActiveSheet.Paste Destination:=Range("A" & LastRow + 1)
End With
End If
End With
End Sub

Resources