I'm trying to create a macro that will pull some data from a pivot table into a collection variable. What I'm wanting to do is to collect each Article's Sales Forecast and Insight each month for a single year (image below). The issue I have is that I don't know how to collect the information without it being shown in the pivot table, and I don't know how to have the macro adjust the column filters to meet my requirements.
I don't have access to the source of this pivot table and I'm reluctant to have the user manually adjust the table's column filters.
For clarification, the column filter goes by Year -> Quarter -> Month -> Week.
Edit:
What I'm trying to accomplish starts with a schedule workbook where the relevant article numbers are kept. The Master Scheduler has a different workbook with the input values (pivot table above) to base the calculations. This input workbook only has pivot tables and I don't know where it pulls the data; just this is where the Master Scheduler manually finds the relevant data.
My thought process is to pull the relevant articles from the scheduling workbook, pull all of the articles for one full year, sort out only the relevant articles, and place the sorted information into a new location in the Schedule workbook.
The issue I'm coming across is that the column filters may not be showing all of the relevant data and I don't know how to make VBA set the pivot table's filter to prevent this. The solutions that I have found changes the F9 cell, "Year," to an actual year value, which is not what I want to do.
In addition, when you manually open the column filter to select which year, there are four quarters (or seasons) where all four must have a check mark in the box for all twelve of the months to be available.
Hopefully this clarifies my question.
After some more research, I found this website that answers my question: Expand and Collapse Entire Pivot Table Fields – VBA Macro. I've also learned about the pt.ClearAllFilters command.
If the website becomes unavailable in the future, the code is also listed below.
Expand:
Sub Expand_Entire_RowField()
'Expand the lowest position field in the Rows area
'that is currently expanded (showing details)
Dim pt As PivotTable
Dim pf As PivotField
Dim pi As PivotItem
Dim iFieldCount As Long
Dim iPosition As Long
'Create reference to 1st pivot table on sheet
'Can be changed to reference a specific sheet or pivot table.
Set pt = ActiveSheet.PivotTables(1)
'Count fields in Rows area minus 1 (last field can't be expanded)
iFieldCount = pt.RowFields.Count - 1
'Loop by position of field
For iPosition = 1 To iFieldCount
'Loop fields in Rows area
For Each pf In pt.RowFields
'If position matches first loop variable then
If pf.Position = iPosition Then
'Loop each pivot item
For Each pi In pf.PivotItems
'If pivot item is collapsed then
If pi.ShowDetail = False Then
'Expand entire field
pf.ShowDetail = True
'Exit the macro
Exit Sub
End If
Next pi
End If
Next pf
'If the Exit Sub line is not hit then the
'loop will continue to the next field position
Next iPosition
End Sub
Collapse:
Sub Collapse_Entire_RowField()
'Collapse the lowest position field in the Rows area
'that is currently expanded (showing details)
Dim pt As PivotTable
Dim pf As PivotField
Dim pi As PivotItem
Dim iFieldCount As Long
Dim iPosition As Long
'Create reference to 1st pivot table on sheet
'Can be changed to reference a specific sheet or pivot table.
Set pt = ActiveSheet.PivotTables(1)
'Count fields in Rows area minus 1 (last field can't be expanded)
iFieldCount = pt.RowFields.Count - 1
'Loop backwards by position of field (step -1)
For iPosition = iFieldCount To 1 Step -1
'Loop fields in Rows area
For Each pf In pt.RowFields
'If position matches first loop variable then
If pf.Position = iPosition Then
'Loop each pivot item
For Each pi In pf.PivotItems
'If pivot item is expanded then
If pi.ShowDetail = True Then
'Collapse entire field
pf.ShowDetail = False
'Exit the macro
Exit Sub
End If
Next pi
End If
Next pf
'If the Exit Sub line is not hit then the
'loop will continue to the next field position
Next iPosition
End Sub
Edit: Solution Specific
Dim pt As PivotTable
Dim pf As PivotField
Set pt = ws.PivotTables(1)
'Setting all of the column filters to desired setup
For Each pf In pt.ColumnFields
pf.Hidden = False
pf.ClearAllFilters
'Drill Down until Months are opened, then exit the loop
If UBound(Split(pf.Name, "Month")) > 0 Then
pf.DrilledDown = False '<-- Ensuring nothing beneath "Months" is
Exit For
Else
pf.DrilledDown = True
End If
Next
Related
I have two Pivot Table (PivotTable1 and PivotTable2) in Sheet PivotTables_Sheetand a cell that shows the year in Sheet YearInput. I tried to link the cell year (J4) in YearInput as a filter to all pivot tables in PivotTables Sheet.
But somehow my code doesn't work as expected. Anybody knows how to correct this code?
This is the code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Target.Address = Range("J4").Address Then Exit Sub
Dim pt As PivotTable
Dim ptItem As PivotItem
On Error Resume Next
For Each pt In Worksheets("PivotTables_Sheet").PivotTables
With pt.PivotFields("Year for Sales")
If .EnableMultiplePageItems = True Then
.ClearAllFilters
End If
Set ptItem = .PivotItems(Target.Value)
If Not ptItem Is Nothing Then
.CurrentPage = Target.Value
End If
End With
Next
End Sub
Note: I use Excel 2010 and built the pivot tables using PowerPivot.
One way to do this without VBA is to use a slicer which is common between all the pivots. Then one year is changed on one, it will also update the other pivot tables.
This is by first clicking on the pivot, which will then show on the ribbon an Analyze option. Under that there is an insert slicer, which you would do for year.
Afterwards you would go to your other pivots and instead do filter connection, and select your year slicer.
Note that the year slicer can then just be moved to a hidden sheet if you do not want the end user to see it, it will still do its job of propagating the year across all the pivots that are referencing it.
I have some VBA code which needs to select Prod. Code, Lims#, SampleID, Log Date, District Name, Region, Machine ID, Ash, cNDF, CP, Ca, Cl from a pivot table.
When I use Entire row, I can select all the data that I need to make a clean paste into column A. However, now I'd like to paste into column
.PivotItems(PivotFieldName.Caption).DataRange.EntireRow.Select
I've attempted to do just data range, however it won't pull Prod.Code, Lims#, etc columns. It will only grab the analyte columns.
.PivotItems(PivotFieldName.Caption).DataRange.Select
I also tried to do a range off of the EntireRow, however then I can't figure out how to get last column or last row for that selected Prod. Code.
.PivotItems(PivotFieldName.Caption).DataRange.EntireRow.Range("A1:BB1").Select
How can I got about getting all columns for a Prod. code selected like the Entire Row method excluding the extra rows?
The Prod.Code Columns & Rows can shift in size depending on the data so I need to make it capable of grabbing the right range.
Further code structure:
Dim PvtTbl As PivotTable
Dim PvtFld As PivotField
Set PvtTbl = Sheets("NEP Pivot").PivotTables("NEP_Pivot")
Set PvtFld = PvtTbl.PivotFields("Prod. Code")
For Each PivotFieldName In PvtFld.PivotItems
'PivotFieldName.Caption represents "EHB"
If IsError(Application.Match(PivotFieldName.Caption, rngList, 0)) Then
With PvtFld
On Error Resume Next
.PivotItems(PivotFieldName.Caption).ShowDetail = True 'Show pivot item
.PivotItems(PivotFieldName.Caption).DataRange.EntireRow.Copy
Destination:=Sheets(PivotFieldName.Caption).Range("A3")
...
..
.
Use the .DataBodyRange property instead to get all the data within the pivot
PivotTable.DataBodyRange Property (Excel)
Returns a Range object that represents the range of values in a
PivotTable. Read-only.
Syntax
expression . DataBodyRange
expression A variable that represents a PivotTable object.
As you also want the row element you can do the following you can resize the data body range according to the difference in column count between the larger TableRange2 property and that inside pivot area. Be aware that grand totals etc may affect this but this shows you how to start thinking about it. There is also a TableRange1 property available with pivots.
Example pivot:
Code:
Option Explicit
Sub test()
Dim ws As Worksheet
Dim pvt As PivotTable
Dim columnsDifference As Long
Set ws = ActiveSheet
Set pvt = ws.PivotTables("PivotTable4")
columnsDifference = pvt.TableRange2.Columns.Count - pvt.DataBodyRange.Columns.Count
With pvt.DataBodyRange
Debug.Print .Offset(, -columnsDifference).Resize(.Rows.Count, .Columns.Count + columnsDifference).Address
End With
End Sub
I would recommend reading up on referencing pivottable ranges in VBA.
There are a variety of properties that you can use to determine ranges e.g. LabelRange and DataRange for fieldItems. You seems to have explored some of these. My experience has been that these are influenced by your pivottable layout and you will need to determine the current combination of methods to get your data.
For example, I used the following to get all the data for EHB with data laid out as follows:
Option Explicit
Sub test()
Dim ws As Worksheet
Dim pvt As PivotTable
Dim columnsDifference As Long
Dim wb As Workbook
Set wb = ThisWorkbook
Set ws = wb.Worksheets("mySheetName")
Set pvt = ws.PivotTables("NEP_Pivot")
columnsDifference = pvt.TableRange2.Columns.Count - pvt.DataBodyRange.Columns.Count
With pvt.DataBodyRange
Debug.Print pvt.PivotFields("Prod. Code").PivotItems("EHB").LabelRange.Offset(-1, 0).Resize(pvt.PivotFields("Prod. Code").PivotItems("EHB").LabelRange.Rows.Count, .Columns.Count + columnsDifference).Address
End With
End Sub
This produced the result
$C$11:$N$13
Notice how I have had to combine functions to get this result and if the pivottable layout gets changed this falls apart.
I was also able to write using:
With pvt.TableRange1
Debug.Print pvt.PivotFields("Prod. Code").PivotItems("EHB").LabelRange.Resize(pvt.PivotFields("Prod. Code").PivotItems("EHB").LabelRange.Rows.Count, .Columns.Count).Offset(-1, 0).Address
End With
I am trying to filter my Pivot Table using VBA so it will filter based on a Period and a Year in cells D2 and E2 respectively. I have the below code which is to filter by the year and works without having the Fiscal Period in the Columns but when I put the Fiscal Period in the columns it debugs at the Field.ClearAllFilters and Field.CurrentPage = Year lines. Can someone please help me find a fix for this?
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Intersect(Target, Worksheets("CY PT").Range("E2")) Is Nothing Then Exit Sub
Dim PT As PivotTable
Dim Field As PivotField
Dim Year As String
Set PT = Worksheets("CY PT").PivotTables("CY PT")
Set Field = PT.PivotFields("Fiscal Year")
Year = Worksheets("CY PT").Range("E2").Value
With PT
Field.ClearAllFilters
Field.CurrentPage = Year
PT.RefreshTable
End With
End Sub
The .CurrentPage method only applies to PageFields. Sounds like you are trying to filter RowFields. So your options are:
Change your fields to be PageFields; or
Use a Slicer; or
Use a PageField filter dropdown mascarading as a Data Validation
dropdown (like I outline at
http://dailydoseofexcel.com/archives/2014/08/16/sync-pivots-from-dropdown/
); or
Iterate through the PivotItems collection for each field, and set the
.Visible status of everything but the selected items to FALSE.
If you choose the last option, you need to be aware this can take a while. See the code I posted at Filtering a Pivot Table Based on Variable Range to get an understanding of how to do this efficiently. Also give my post at http://dailydoseofexcel.com/archives/2013/11/14/filtering-pivots-based-on-external-ranges/ a read.
Sheet 1 holds a full list of the current state of work orders
Sheet 2 holds recent changes to those work orders and any new work orders
Both sheets have the same format with data in columns A to L.
I need to use Sheet 2 to update the full list held in Sheet 1. Work orders have a unique identifier which is held in column A of each sheet.
In general terms:
Process each row of Sheet 2.
If a matching work order already exists in Sheet 1, update it.
If no matching work order exists in Sheet 1, add it as a new row in Sheet 1.
In column A is the work order number.
There may be better ways to do this, and as #Jeeped said, this has probably been asked before (though I couldn't find it). Hopefully the following is what you need. I've included lots of comments to help you understand the code and modify it should you need to.
Sub ProcessDelta()
'Define the worksheets
Dim shtDelta As Worksheet
Dim shtMaster As Worksheet
Set shtDelta = Worksheets("Sheet2")
Set shtMaster = Worksheets("Sheet1")
Dim intDeltaStartRow As Integer
'I assume there is a header row in the Delta sheet, if not, set this to 1
intDeltaStartRow = 2
Dim intMaxEverWorkOrders As Integer
'One of several ways to find the first blank row in the Master
'sheet is to start somewhere beyond the data and move up
'we use this later
intMaxEverWorkOrders = 1000000
Dim cellDeltaWorkOrder As Range
'Work order from Delta to be processed
Set cellDeltaWorkOrder = shtDelta.Cells(intDeltaStartRow, 1)
'For the destination to which we copy
Dim cellMasterWorkOrder As Range
Dim boolNewWorkOrder As Boolean
'Default to assume it's not a new workorder
boolNewWorkOrder = False
'We'll work from top to bottom in the Delta sheet. When the cell is blank we've finished
While cellDeltaWorkOrder.Value <> ""
'We're going to search for the "current" workorder from the Delta in the Master.
'If it's not there, we'll get an error. So we use "On Error" to handle it
On Error GoTo ErrorStep
'If there is no error, after the following line cellMasterWorkOrder will be the cell containing the matching workorder
Set cellMasterWorkOrder = shtMaster.Cells(WorksheetFunction.Match(cellDeltaWorkOrder.Value, shtMaster.Cells(1, 1).EntireColumn, 0), 1) '
'Reset error handling so any other errors are reported normally
On Error GoTo 0
'Check whether there was an error, if there was this was a new Workorder and needs to go at the end, so set the target cell accordingly
If boolNewWorkOrder = True Then
Set cellMasterWorkOrder = shtMaster.Cells(intMaxEverWorkOrders, 1).End(xlUp).Offset(1, 0)
boolNewWorkOrder = False 'reset this so we can check again for the next row to be processed
End If
'Output Row into Master
cellMasterWorkOrder.EntireRow.Value = cellDeltaWorkOrder.EntireRow.Value
'Move to next row in the Delta
Set cellDeltaWorkOrder = cellDeltaWorkOrder.Offset(1, 0)
Wend
'We don't want to run the error step at this point so ..
Exit Sub
ErrorStep:
'It wasn't found, which throws an error, and so it needs added as a new row.
boolNewWorkOrder = True
Resume Next
End Sub
I have data of this form:
Category Source Amount
Dues FTW $100
Donations ODP $20
Donations IOI $33
Dues MMK $124
There is no sort order. The categories are unknown at compile time. I want a VBA macro to cycle through the range, output a list of distinct values, with the subtotals for each. For the above data, it would look like this:
Category Total Amount
Dues $224
Donations $55
How can I do this? Also, is there a way to make the macro run every time the table above is updated, or is it necessary for the user to click a button?
You may want to use a built in Excel feature to do this. Looping can take a long time and be problematic if you have a lot of values to loop through.
Something like the following might get you started on creating a pivot table (from http://www.ozgrid.com/News/pivot-tables.htm)
Sub MakeTable()
Dim Pt As PivotTable
Dim strField As String
'Pass heading to a String variable
strField = Selection.Cells(1, 1).Text
'Name the list range
Range(Selection, Selection.End(xlDown)).Name = "Items"
'Create the Pivot Table based off our named list range.
'TableDestination:="" will force it onto a new sheet
ActiveWorkbook.PivotCaches.Add(SourceType:=xlDatabase, _
SourceData:="=Items").CreatePivotTable TableDestination:="", _
TableName:="ItemList"
'Set a Pivot Table variable to our new Pivot Table
Set Pt = ActiveSheet.PivotTables("ItemList")
'Place the Pivot Table to Start from A3 on the new sheet
ActiveSheet.PivotTableWizard TableDestination:=Cells(3, 1)
'Move the list heading to the Row Field
Pt.AddFields RowFields:=strField
'Move the list heading to the Data Field
Pt.PivotFields(strField).Orientation = xlDataField
End Sub
This is for subtotals (though I much prefer pivot tables)
http://msdn.microsoft.com/en-us/library/aa213577(office.11).aspx
EDIT:
I reread and have the following thoughts. Set up the pivot table on a separate sheet (without using any code) then put the following code on the sheet that has the pivot table so that the table will update everytime the sheet is selected.
Private Sub Worksheet_Activate()
Dim pt As PivotTable
'change "MiPivot" to the name of your pivot table
Set pt = ActiveSheet.PivotTables("MyPivot")
pt.RefreshTable
End Sub
Edit #2
Refresh all pivot tables on sheet
http://www.ozgrid.com/VBA/pivot-table-refresh.htm
Private Sub Worksheet_Activate()
Dim pt As PivotTable
For Each pt In ActiveSheet.PivotTables
pt.RefreshTable
Next pt
End Sub
The link has multiple options on how to refresh pivot tables in the sheet/workbook.