Inventory Management
Inventory Management

Inventory Management Is Broken for Many Businesses — This Excel VBA Dashboard Changes Everything 2026

Inventory Management:- Businesses generate thousands of inventory transactions every month.

Products move in and out of warehouses, suppliers deliver stock, sales teams generate revenue, and managers try to make sense of endless spreadsheets.

The problem?

Most inventory data sits inside Excel sheets without delivering meaningful business insights.

A spreadsheet can tell you what happened.

A dashboard can tell you why it happened and what to do next.

That’s exactly why I built the Inventory Intelligence Center, a fully automated Excel VBA dashboard designed to transform raw inventory data into actionable business intelligence.

Using a simple dataset containing Month, Category, Product, Supplier, Revenue, Profit, Expenses, Stock In, and Stock Out information, the dashboard automatically creates executive-level insights that help businesses monitor performance, improve profitability, and make smarter decisions.


Why Inventory Visibility Matters More Than Ever

Inventory management is no longer just about tracking stock levels.

Modern businesses must answer questions like:

  • Which product generates the highest revenue?
  • Which category delivers the best profit margin?
  • Which supplier contributes the most value?
  • Are inventory levels increasing faster than sales?
  • Which month performs best?
  • Where are expenses reducing profitability?

Without clear visibility, companies often face:

  • Overstocking
  • Stock shortages
  • Reduced cash flow
  • Poor purchasing decisions
  • Lower profit margins
  • Delayed reporting

The Inventory Intelligence Center solves these challenges by converting raw inventory records into real-time visual insights.


From Raw Data to Business Intelligence

The dashboard starts with a simple inventory table.

Typical data includes:

Most businesses stop here.

But this dashboard automatically transforms these records into interactive reports, KPI cards, charts, slicers, and executive insights.

No manual Pivot Table creation.

No repetitive reporting.

No complicated formulas.

Just one click.


Executive Dashboard at First Glance

When users open the Inventory Intelligence Center, they immediately see the most important business metrics.

Total Revenue

Revenue provides an instant overview of business performance.

The dashboard automatically calculates overall sales generated from all inventory transactions.

Instead of filtering rows or creating formulas manually, users can instantly see how much revenue the business has generated.


Total Profit

Revenue alone doesn’t tell the whole story.

Profit determines business success.

The dashboard tracks total profit and highlights overall profitability across all products and categories.

This allows business owners to understand whether growth is actually generating value.


Units Sold

Inventory movement is critical.

The dashboard calculates total units sold and provides immediate visibility into demand patterns.

Businesses can quickly identify whether inventory turnover is improving or slowing down.


Total Expenses

Expenses directly impact profitability.

Tracking operational costs is essential for sustainable growth.

The dashboard automatically summarizes expenses and displays them alongside revenue and profit for complete financial visibility.


Profit Margin

One of the most important KPIs in inventory management is profit margin.

The dashboard automatically calculates profit margin percentages, helping users evaluate efficiency rather than simply focusing on sales volume.

A business generating higher margins often performs better than one generating higher revenue with excessive costs.


Revenue and Profit Trend Analysis

One of the most powerful features is the Revenue & Profit Trend chart.

This visualization helps identify performance patterns throughout the year.

Instead of reviewing monthly reports individually, users can instantly spot:

  • Revenue growth trends
  • Profit fluctuations
  • Expense changes
  • Stock-out patterns

This information helps management answer critical questions.

Is revenue growing consistently?

A rising trend indicates healthy business growth.

Are expenses increasing too quickly?

Expense spikes can highlight operational inefficiencies.

Which months perform best?

Seasonal demand becomes visible immediately.

Are profits keeping pace with revenue?

Revenue growth without profit growth may indicate pricing or cost issues.

The chart transforms static numbers into meaningful business insights.


Revenue by Category: Understanding What Drives Sales

Not all product categories contribute equally.

Some categories generate most of the revenue while others consume resources without delivering significant returns.

The Revenue by Category visualization automatically breaks down sales performance across categories such as:

  • Electronics
  • Furniture
  • Appliances
  • Accessories
  • Office Supplies

This allows decision-makers to identify:

Top Revenue Categories

Understanding where revenue originates helps allocate resources effectively.

Underperforming Categories

Poor-performing categories can be reviewed for optimization.

Growth Opportunities

Categories showing strong momentum can receive additional investment.

This level of visibility is often unavailable in traditional inventory reports.


Product Performance Ranking

Every inventory manager wants to know one thing:

Which products make the most money?

The Top Products by Revenue chart answers that question immediately.

Products are automatically ranked based on performance.

Benefits include:

  • Identifying best sellers
  • Monitoring demand trends
  • Optimizing stock levels
  • Improving purchasing decisions
  • Increasing inventory turnover

Rather than guessing which products deserve attention, businesses can use actual performance data.

This significantly improves inventory planning.


Supplier Performance Insights

Suppliers play a major role in inventory success.

A reliable supplier can improve profitability.

An inefficient supplier can create stock shortages and operational challenges.

The Supplier Revenue section tracks supplier performance using revenue and profit metrics.

Management can evaluate:

  • Revenue generated per supplier
  • Profit contribution
  • Supplier dependency
  • Purchasing efficiency

These insights support better supplier negotiations and procurement strategies.

Businesses can focus on vendors delivering the highest value.


Inventory Flow Monitoring

Inventory movement directly impacts cash flow.

The Inventory Flow section visualizes:

Stock In

Products entering inventory.

Stock Out

Products leaving inventory through sales or usage.

Tracking these movements helps businesses identify:

  • Overstock situations
  • Fast-moving products
  • Inventory shortages
  • Replenishment requirements

Instead of waiting for monthly inventory reviews, managers can monitor stock activity continuously.


Revenue vs Expenses: The True Profitability Indicator

Many businesses focus only on sales numbers.

The problem is simple:

High revenue doesn’t always mean high profit.

The Revenue vs Expenses chart helps businesses evaluate financial performance more accurately.

Users can instantly compare:

  • Revenue
  • Expenses
  • Profit contribution

This helps identify:

  • Cost-control opportunities
  • Margin improvements
  • Expense spikes
  • Operational inefficiencies

Data-driven decisions become significantly easier when financial performance is visualized clearly.


Executive Insights: Automated Business Intelligence

Perhaps the most powerful feature of the Inventory Intelligence Center is the Executive Insights section.

Instead of manually analyzing multiple reports, the dashboard automatically identifies key findings.

Examples include:

Highest Revenue Category

Shows the category generating maximum sales.

Best Performing Product

Highlights the strongest product based on revenue.

Top Supplier

Identifies the supplier contributing the most business value.

Most Profitable Category

Reveals which category delivers the highest profit.

Best Revenue Month

Highlights peak sales periods.

Lowest Expense Month

Shows periods of operational efficiency.

These insights provide immediate executive-level decision support.


Interactive Filtering for Instant Analysis

Modern dashboards must be interactive.

That’s why the Inventory Intelligence Center includes smart slicers for:

  • Month
  • Category
  • Supplier
  • Product

Users can click a filter and instantly update:

  • KPI cards
  • Charts
  • Rankings
  • Executive insights

No formulas need to be edited.

No Pivot Tables need to be refreshed manually.

Everything updates automatically.


Why Excel VBA Is Still a Powerful Business Tool

Many organizations believe advanced analytics requires expensive software.

In reality, Excel VBA remains one of the most powerful business intelligence tools because it offers:

Affordability

No costly software subscriptions.

Flexibility

Dashboards can be customized for any business.

Automation

Reduces manual reporting effort.

Familiar Interface

Most teams already use Excel.

Scalability

Handles thousands of inventory transactions efficiently.

For small and medium-sized businesses, VBA dashboards provide enterprise-level reporting without enterprise-level costs.


The Future of Inventory Management

Businesses that rely on manual reporting will struggle to compete.

The future belongs to organizations that can:

  • Analyze data faster
  • Identify trends earlier
  • Improve inventory turnover
  • Reduce operational costs
  • Increase profitability

Inventory intelligence is no longer optional.

It is becoming a competitive advantage.

The Inventory Intelligence Center demonstrates how Excel can evolve from a simple spreadsheet application into a complete business intelligence platform.

VBA Code

Option Explicit

' =============================================================================
'  INVENTORY INTELLIGENCE CENTER v7.0  -  PREMIUM EDITION (FIXED)
'  Fixes vs v6:
'   1. Insights panel & small charts were overlapping (both started at the
'      same row). Insights now has its own dedicated band (rows 51-59);
'      small charts moved to a NEW band below (rows 61-79). Footer and total
'      sheet height shifted down accordingly so nothing collides.
'   2. Slicers had excess empty space below items + looked flat/uncolored.
'      Height is now calculated from the ACTUAL item count per field
'      (capped sensibly). Styling uses the genuine built-in slicer style
'      "SlicerStyleDark3" (a real, verified Excel constant string) for
'      strong dark/navy contrast -- no fabricated SlicerStyle/TableStyle
'      objects are used, since those do not exist in the VBA object model.
'   3. Large charts (Top Products, etc.) felt cramped with many bars/labels.
'      Chart row bands heightened and category-axis fonts tightened so bars
'      render thicker and more professional.
' =============================================================================

Private Const SHEET_DATA      As String = "Data"
Private Const SHEET_PIVOT     As String = "PivotData"
Private Const SHEET_DASHBOARD As String = "Dashboard"

Private Const COL_MONTH    As String = "Month"
Private Const COL_CATEGORY As String = "Category"
Private Const COL_PRODUCT  As String = "Product"
Private Const COL_SUPPLIER As String = "Supplier"
Private Const COL_REVENUE  As String = "Revenue"
Private Const COL_PROFIT   As String = "Profit"
Private Const COL_EXPENSES As String = "Expenses"
Private Const COL_STOCKIN  As String = "Stock In"
Private Const COL_STOCKOUT As String = "Stock Out"

Private Function C(r As Integer, g As Integer, b As Integer) As Long
    C = RGB(r, g, b)
End Function

' =============================================================================
'  MAIN
' =============================================================================
Public Sub BuildInventoryIntelligenceCenter()
    Dim wb As Workbook, wsData As Worksheet
    Dim wsPivot As Worksheet, wsDash As Worksheet
    Dim T As Double: T = Timer

    On Error GoTo ErrHandler
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual
    Application.DisplayAlerts = False
    Application.EnableEvents = False

    Set wb = ThisWorkbook

    Set wsData = GetWS(SHEET_DATA)
    If wsData Is Nothing Then
        MsgBox "Sheet 'Data' not found!", vbCritical, "IIC": GoTo CleanUp
    End If
    If Not ValidateHeaders(wsData) Then GoTo CleanUp

    DeleteAssets wb

    Set wsPivot = wb.Worksheets.Add
    wsPivot.Name = SHEET_PIVOT
    BuildAllPivots wb, wsData, wsPivot

    ' Force calculation so pivot ranges are real before we read them
    Application.Calculation = xlCalculationAutomatic
    wb.RefreshAll
    Application.Calculate
    Application.Calculation = xlCalculationManual

    wsPivot.Columns.AutoFit
    wsPivot.Tab.Color = C(0, 137, 123)

    Set wsDash = wb.Worksheets.Add(Before:=wb.Sheets(1))
    wsDash.Name = SHEET_DASHBOARD

    BuildLayout wsDash
    BuildKPICards wsDash, wsPivot
    BuildCharts wsDash, wsPivot
    BuildSlicers wb, wsDash, wsPivot
    BuildInsights wsDash, wsPivot
    FinalPolish wsDash, wsPivot

    On Error Resume Next
    Application.Calculation = xlCalculationAutomatic
    wb.RefreshAll
    Application.Calculate
    On Error GoTo 0

    MsgBox "Inventory Intelligence Center v6.0 Ready!" & vbLf & _
           "Build time: " & Format(Timer - T, "0.0") & "s", _
           vbInformation, "IIC"

CleanUp:
    Application.ScreenUpdating = True
    Application.Calculation = xlCalculationAutomatic
    Application.DisplayAlerts = True
    Application.EnableEvents = True
    Exit Sub
ErrHandler:
    Application.ScreenUpdating = True
    Application.Calculation = xlCalculationAutomatic
    Application.DisplayAlerts = True
    Application.EnableEvents = True
    MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "IIC"
    Resume CleanUp
End Sub

' =============================================================================
'  VALIDATE HEADERS
' =============================================================================
Private Function ValidateHeaders(ws As Worksheet) As Boolean
    Dim cols() As String
    cols = Split(COL_MONTH & "," & COL_CATEGORY & "," & COL_PRODUCT & "," & _
                 COL_SUPPLIER & "," & COL_REVENUE & "," & COL_PROFIT & "," & _
                 COL_EXPENSES & "," & COL_STOCKIN & "," & COL_STOCKOUT, ",")
    Dim lastCol As Long: lastCol = GetLastCol(ws, 1)
    Dim missing As String, i As Integer, j As Long, found As Boolean
    For i = 0 To UBound(cols)
        found = False
        For j = 1 To lastCol
            If Trim(ws.Cells(1, j).Value) = Trim(cols(i)) Then found = True: Exit For
        Next j
        If Not found Then missing = missing & "  - " & cols(i) & vbLf
    Next i
    If Len(missing) > 0 Then
        MsgBox "Missing columns:" & vbLf & missing, vbCritical, "IIC"
        ValidateHeaders = False
    Else
        ValidateHeaders = True
    End If
End Function

' =============================================================================
'  DELETE ASSETS
' =============================================================================
Private Sub DeleteAssets(wb As Workbook)
    On Error Resume Next
    Dim sc As SlicerCache
    For Each sc In wb.SlicerCaches: sc.Delete: Next sc
    Dim ws As Worksheet
    For Each ws In wb.Worksheets
        If ws.Name = SHEET_DASHBOARD Or ws.Name = SHEET_PIVOT Then ws.Delete
    Next ws
    On Error GoTo 0
End Sub

' =============================================================================
'  BUILD PIVOT TABLES
'  Pivots are spaced 40 rows apart (instead of cramped) so each one always
'  has plenty of room regardless of how many categories/products exist.
' =============================================================================
Private Sub BuildAllPivots(wb As Workbook, wsData As Worksheet, wsPivot As Worksheet)
    Dim lastRow As Long: lastRow = GetLastRow(wsData, 1)
    Dim lastCol As Long: lastCol = GetLastCol(wsData, 1)
    Dim pc As PivotCache
    Set pc = wb.PivotCaches.Create(xlDatabase, _
        wsData.Range(wsData.Cells(1, 1), wsData.Cells(lastRow, lastCol)))

    Dim pt As PivotTable

    Set pt = MakePT(pc, wsPivot, "A1", "PT_SalesTrend")
    pt.PivotFields(COL_MONTH).Orientation = xlRowField
    AddDF pt, COL_REVENUE, "Sum of Revenue"
    AddDF pt, COL_PROFIT, "Sum of Profit"
    AddDF pt, COL_EXPENSES, "Sum of Expenses"
    AddDF pt, COL_STOCKOUT, "Sum of Stock Out"

    Set pt = MakePT(pc, wsPivot, "A40", "PT_CategoryAnalysis")
    pt.PivotFields(COL_CATEGORY).Orientation = xlRowField
    AddDF pt, COL_REVENUE, "Sum of Revenue"
    AddDF pt, COL_PROFIT, "Sum of Profit"

    Set pt = MakePT(pc, wsPivot, "A70", "PT_ProductRanking")
    pt.PivotFields(COL_PRODUCT).Orientation = xlRowField
    AddDF pt, COL_REVENUE, "Sum of Revenue"
    AddDF pt, COL_PROFIT, "Sum of Profit"
    AddDF pt, COL_STOCKOUT, "Sum of Stock Out"

    Set pt = MakePT(pc, wsPivot, "A130", "PT_SupplierPerformance")
    pt.PivotFields(COL_SUPPLIER).Orientation = xlRowField
    AddDF pt, COL_REVENUE, "Sum of Revenue"
    AddDF pt, COL_PROFIT, "Sum of Profit"

    Set pt = MakePT(pc, wsPivot, "A160", "PT_InventoryFlow")
    pt.PivotFields(COL_MONTH).Orientation = xlRowField
    AddDF pt, COL_STOCKIN, "Sum of Stock In"
    AddDF pt, COL_STOCKOUT, "Sum of Stock Out"

    Set pt = MakePT(pc, wsPivot, "A190", "PT_Profitability")
    pt.PivotFields(COL_CATEGORY).Orientation = xlRowField
    AddDF pt, COL_PROFIT, "Sum of Profit"
End Sub

Private Function MakePT(pc As PivotCache, ws As Worksheet, _
                         addr As String, ptName As String) As PivotTable
    On Error Resume Next
    Dim pt As PivotTable
    Set pt = pc.CreatePivotTable(TableDestination:=ws.Range(addr), TableName:=ptName)
    If Not pt Is Nothing Then
        pt.TableStyle2 = "PivotStyleMedium9"
        pt.ShowDrillIndicators = False
        pt.DisplayFieldCaptions = False
        pt.ColumnGrand = True
        pt.RowGrand = False
    End If
    Set MakePT = pt
    On Error GoTo 0
End Function

Private Sub AddDF(pt As PivotTable, fld As String, cap As String)
    On Error Resume Next
    Dim df As PivotField
    Set df = pt.AddDataField(pt.PivotFields(fld), cap, xlSum)
    If Not df Is Nothing Then df.NumberFormat = "#,##0"
    On Error GoTo 0
End Sub

' =============================================================================
'  DASHBOARD LAYOUT
' =============================================================================
Private Sub BuildLayout(ws As Worksheet)
    ws.Cells.Interior.Color = C(244, 246, 251)

    ws.Rows(1).RowHeight = 5
    ws.Rows(2).RowHeight = 54
    ws.Rows(3).RowHeight = 16
    ws.Rows(4).RowHeight = 8
    ws.Rows(5).RowHeight = 6

    Dim r As Long
    For r = 6 To 14:  ws.Rows(r).RowHeight = 13: Next r
    ws.Rows(15).RowHeight = 10

    For r = 16 To 33: ws.Rows(r).RowHeight = 14.5: Next r
    ws.Rows(34).RowHeight = 6

    For r = 35 To 49: ws.Rows(r).RowHeight = 16: Next r
    ws.Rows(50).RowHeight = 6

    ' Insights band: rows 51-59 (dedicated, nothing else here)
    For r = 51 To 59: ws.Rows(r).RowHeight = 15: Next r
    ws.Rows(60).RowHeight = 8   ' spacer between insights and small charts

    ' Small-charts band: rows 61-79 (separate from insights)
    For r = 61 To 79: ws.Rows(r).RowHeight = 14.5: Next r
    ws.Rows(80).RowHeight = 6
    ws.Rows(81).RowHeight = 22   ' footer

    Dim col As Long
    For col = 1 To 24: ws.Columns(col).ColumnWidth = 7.5: Next col
    For col = 25 To 28: ws.Columns(col).ColumnWidth = 8.8: Next col

    ws.Range("Y1:AB81").Interior.Color = C(22, 40, 70)

    With ws.Range("A2:X2")
        .Merge
        .Value = "   INVENTORY INTELLIGENCE CENTER"
        .Interior.Color = RGB(0, 137, 123)
        .Font.Name = "Calibri": .Font.Size = 22
        .Font.Bold = True: .Font.Color = RGB(245, 247, 245)
        .VerticalAlignment = xlVAlignCenter
        .HorizontalAlignment = xlHAlignLeft
    End With
    With ws.Range("Y2:AB2")
        .Interior.Color = RGB(0, 137, 123)
    End With

    With ws.Range("A4:X4")
        .Merge
        .Value = "Last refreshed: " & Format(Now, "dd-mmm-yyyy hh:mm")
        .Interior.Color = C(244, 246, 251)
        .Font.Name = "Calibri": .Font.Size = 7
        .Font.Italic = True: .Font.Color = C(100, 110, 130)
        .HorizontalAlignment = xlHAlignRight
        .VerticalAlignment = xlVAlignCenter
    End With

    With ws.Range("A3:X3")
        .Merge
        .Value = "   Real-Time Inventory & Sales Performance  |  Powered by Excel Tricks"
        .Interior.Color = C(123, 137, 123)
        .Font.Name = "Calibri": .Font.Size = 8
        .Font.Color = RGB(255, 255, 255)
        .VerticalAlignment = xlVAlignCenter
        .HorizontalAlignment = xlHAlignLeft
    End With
    With ws.Range("Y3:AB3")
        .Interior.Color = C(0, 137, 123)
    End With

    With ws.Range("A81:AB81")
        .Merge
        .Value = "   INVENTORY INTELLIGENCE CENTER  |  Enterprise VBA Dashboard  |  " & _
                 Format(Now, "dd-mmm-yyyy hh:mm")
        .Interior.Color = C(13, 27, 42)
        .Font.Name = "Calibri": .Font.Size = 8
        .Font.Color = RGB(255, 255, 255)
        .VerticalAlignment = xlVAlignCenter
    End With
End Sub

' =============================================================================
'  KPI CARDS
'  FIX: NumberFormat is now applied ONLY for numeric cards, and AFTER value
'  population (never "@" before a formula is written -> that bug forced
'  Excel to store formulas as literal text instead of evaluating them).
' =============================================================================
Private Sub BuildKPICards(wsDash As Worksheet, wsPivot As Worksheet)

    Dim titles(1 To 6) As String
    Dim colors(1 To 6) As Long
    Dim cStart(1 To 6) As Integer

    titles(1) = "TOTAL REVENUE": colors(1) = C(0, 137, 123)
    titles(2) = "TOTAL PROFIT": colors(2) = C(46, 125, 50)
    titles(3) = "UNITS SOLD": colors(3) = C(249, 168, 37)
    titles(4) = "TOTAL EXPENSES": colors(4) = C(183, 28, 28)
    titles(5) = "PROFIT MARGIN": colors(5) = C(106, 27, 154)
    titles(6) = "TOP CATEGORY": colors(6) = C(21, 101, 192)

    cStart(1) = 1: cStart(2) = 5: cStart(3) = 9
    cStart(4) = 13: cStart(5) = 17: cStart(6) = 21

    Dim i As Integer
    For i = 1 To 6
        Dim c1 As Integer: c1 = cStart(i)
        Dim c2 As Integer: c2 = c1 + 3

        Dim L  As Double: L = wsDash.Cells(6, c1).Left + 5
        Dim T  As Double: T = wsDash.Cells(6, c1).Top + 3
        Dim W  As Double: W = wsDash.Range(wsDash.Cells(6, c1), wsDash.Cells(6, c2)).Width - 10
        Dim Hh As Double: Hh = wsDash.Range(wsDash.Cells(6, c1), wsDash.Cells(14, c1)).Height - 6

        Dim shShadow As Shape
        Set shShadow = wsDash.Shapes.AddShape(msoShapeRoundedRectangle, L + 3, T + 3, W, Hh)
        With shShadow
            .Fill.ForeColor.RGB = C(190, 200, 220)
            .Fill.Solid
            .Line.Visible = msoFalse
            .Name = "Shadow_" & i
            .ZOrder msoSendToBack
        End With

        Dim shCard As Shape
        Set shCard = wsDash.Shapes.AddShape(msoShapeRoundedRectangle, L, T, W, Hh)
        With shCard
            .Fill.ForeColor.RGB = RGB(255, 255, 255)
            .Fill.Solid
            .Line.ForeColor.RGB = colors(i)
            .Line.Weight = 1.25
            .Line.Visible = msoTrue
            .Name = "Card_" & i
            On Error Resume Next
            .Adjustments(1) = 0.07
            On Error GoTo 0
        End With

        Dim Hdr As Double: Hdr = Hh * 0.38
        Dim shHdr As Shape
        Set shHdr = wsDash.Shapes.AddShape(msoShapeRoundedRectangle, L, T, W, Hdr)
        With shHdr
            .Fill.ForeColor.RGB = colors(i)
            .Fill.Solid
            .Line.Visible = msoFalse
            .Name = "Hdr_" & i
            On Error Resume Next
            .Adjustments(1) = 0.12
            On Error GoTo 0
            .TextFrame.Characters.Text = titles(i)
            With .TextFrame.Characters.Font
                .Name = "Calibri"
                .Size = 8
                .Bold = True
                .Color = RGB(255, 255, 255)
            End With
            .TextFrame.HorizontalAlignment = xlHAlignCenter
            .TextFrame.VerticalAlignment = xlVAlignCenter
        End With

        Dim Tval As Double: Tval = T + Hdr
        Dim Hval As Double: Hval = Hh - Hdr
        Dim shVal As Shape
        Set shVal = wsDash.Shapes.AddTextbox(msoTextOrientationHorizontal, L, Tval, W, Hval)
        With shVal
            .Fill.Visible = msoFalse
            .Line.Visible = msoFalse
            .Name = "Val_" & i
            .TextFrame.Characters.Text = "..."
            With .TextFrame
                .HorizontalAlignment = xlHAlignCenter
                .VerticalAlignment = xlVAlignCenter
                .Characters.Font.Name = "Calibri"
                .Characters.Font.Size = 17
                .Characters.Font.Bold = True
                .Characters.Font.Color = colors(i)
            End With
        End With

        ' Helper cell: GENERAL format, formula written first, format set after
        Dim vc As Range: Set vc = wsDash.Cells(6 + i, 25)
        vc.NumberFormat = "General"
        vc.ClearContents
    Next i

    ' Now write all formulas using GETPIVOTDATA against the live pivot
    Dim pv As String: pv = wsPivot.Name
    Dim anchor As String: anchor = pv & "!$A$3"   ' first data cell area of PT_SalesTrend

    On Error Resume Next
    wsDash.Cells(7, 25).Formula = "=IFERROR(GETPIVOTDATA(""Sum of Revenue""," & pv & "!$A$1),0)"
    wsDash.Cells(8, 25).Formula = "=IFERROR(GETPIVOTDATA(""Sum of Profit""," & pv & "!$A$1),0)"
    wsDash.Cells(9, 25).Formula = "=IFERROR(GETPIVOTDATA(""Sum of Stock Out""," & pv & "!$A$1),0)"
    wsDash.Cells(10, 25).Formula = "=IFERROR(GETPIVOTDATA(""Sum of Expenses""," & pv & "!$A$1),0)"
    wsDash.Cells(11, 25).Formula = "=IFERROR(IF(GETPIVOTDATA(""Sum of Revenue""," & pv & "!$A$1)=0,0," & _
                                    "GETPIVOTDATA(""Sum of Profit""," & pv & "!$A$1)" & _
                                    "/GETPIVOTDATA(""Sum of Revenue""," & pv & "!$A$1)),0)"

    ' Top category: use the Category pivot table's own range dynamically
    Dim ptCat As PivotTable
    Set ptCat = wsPivot.PivotTables("PT_CategoryAnalysis")
    Dim catLabelRange As String, catRevRange As String
    catLabelRange = ptCat.PivotFields(COL_CATEGORY).DataRange.Address(False, False, xlA1, True)
    catRevRange = ptCat.DataBodyRange.Columns(1).Address(False, False, xlA1, True)
    wsDash.Cells(12, 25).Formula = "=IFERROR(INDEX(" & pv & "!" & catLabelRange & _
        ",MATCH(MAX(" & pv & "!" & catRevRange & ")," & pv & "!" & catRevRange & ",0)),""N/A"")"
    On Error GoTo 0

    wsDash.Range(wsDash.Cells(7, 25), wsDash.Cells(11, 25)).NumberFormat = "0.##########"
End Sub

' Called after RefreshAll to fill shape value textboxes
Private Sub PopulateKPIValues(wsDash As Worksheet)
    Dim colors(1 To 6) As Long
    colors(1) = C(0, 137, 123): colors(2) = C(46, 125, 50)
    colors(3) = C(249, 168, 37): colors(4) = C(183, 28, 28)
    colors(5) = C(106, 27, 154): colors(6) = C(21, 101, 192)

    Dim i As Integer
    For i = 1 To 6
        Dim vc As Range
        Set vc = wsDash.Cells(6 + i, 25)

        Dim displayVal As String
        On Error Resume Next
        If i = 6 Then
            displayVal = CStr(vc.Value)
            If displayVal = "" Then displayVal = "N/A"
        ElseIf i = 5 Then
            displayVal = Format(vc.Value * 100, "0.0") & "%"
        Else
            Dim v As Double: v = vc.Value
            If v >= 1000000 Then
                displayVal = Format(v / 1000000, "0.00") & "M"
            ElseIf v >= 1000 Then
                displayVal = Format(v / 1000, "0.0") & "K"
            Else
                displayVal = Format(v, "#,##0")
            End If
        End If
        On Error GoTo 0

        On Error Resume Next
        Dim shVal As Shape
        Set shVal = wsDash.Shapes("Val_" & i)
        If Not shVal Is Nothing Then
            shVal.TextFrame.Characters.Text = displayVal
            With shVal.TextFrame.Characters.Font
                .Name = "Calibri"
                .Size = IIf(i = 6, 13, 16)
                .Bold = True
                .Color = colors(i)
            End With
        End If
        On Error GoTo 0
    Next i
End Sub

' =============================================================================
'  BUILD CHARTS
'  FIX: explicit Chart.SetSourceData with the pivot table's CURRENT TableRange1
'  read fresh (after RefreshAll), plus chart type assigned BEFORE styling and
'  a guaranteed non-zero PlotVisibleOnly setting so empty-looking charts
'  due to filtered/hidden rows don't appear blank.
' =============================================================================
Private Sub BuildCharts(wsDash As Worksheet, wsPivot As Worksheet)
    MakeChart wsDash, wsPivot, "Revenue & Profit Trend", "PT_SalesTrend", xlLineMarkers, 16, 1, 33, 13
    MakeChart wsDash, wsPivot, "Revenue by Category", "PT_CategoryAnalysis", xlDoughnut, 16, 14, 33, 24
    MakeChart wsDash, wsPivot, "Top Products by Revenue", "PT_ProductRanking", xlBarClustered, 35, 1, 49, 13
    MakeChart wsDash, wsPivot, "Supplier Revenue", "PT_SupplierPerformance", xlAreaStacked, 35, 14, 49, 24
End Sub

Private Sub MakeChart(wsDash As Worksheet, wsPivot As Worksheet, _
                       title As String, ptName As String, cType As Long, _
                       r1 As Long, c1 As Long, r2 As Long, c2 As Long)
    On Error GoTo ChartErr
    Dim L As Double: L = wsDash.Cells(r1, c1).Left + 3
    Dim T As Double: T = wsDash.Cells(r1, c1).Top + 3
    Dim W As Double: W = wsDash.Range(wsDash.Cells(r1, c1), wsDash.Cells(r1, c2)).Width - 6
    Dim H As Double: H = wsDash.Range(wsDash.Cells(r1, c1), wsDash.Cells(r2, c1)).Height - 6

    Dim pt As PivotTable
    Set pt = wsPivot.PivotTables(ptName)

    ' Make sure pivot has calculated data before reading its range
    pt.RefreshTable

    Dim co As ChartObject
    Set co = wsDash.ChartObjects.Add(L, T, W, H)
    co.Name = "Cht_" & ptName

    Dim cht As Chart: Set cht = co.Chart
    cht.ChartType = cType
    cht.SetSourceData Source:=pt.TableRange1
    cht.PlotVisibleOnly = False

    StyleChart cht, title
    Exit Sub
ChartErr:
    Debug.Print "Chart error [" & title & "]: " & Err.Description & " (continuing)"
End Sub

Private Sub StyleChart(cht As Chart, titleText As String)
    On Error Resume Next

    cht.ChartArea.Interior.Color = RGB(255, 255, 255)
    cht.PlotArea.Interior.Color = RGB(255, 255, 255)
    cht.ChartArea.Border.Color = C(210, 218, 235)
    cht.ChartArea.Border.Weight = xlThin

    cht.HasTitle = True
    cht.ChartTitle.Text = titleText
    With cht.ChartTitle.Font
        .Name = "Calibri": .Size = 11: .Bold = True: .Color = C(13, 27, 42)
    End With

    Dim pal(1 To 6) As Long
    pal(1) = C(0, 137, 123)
    pal(2) = C(249, 168, 37)
    pal(3) = C(63, 81, 181)
    pal(4) = C(233, 30, 99)
    pal(5) = C(46, 125, 50)
    pal(6) = C(183, 28, 28)

    Dim s As Integer
    For s = 1 To cht.SeriesCollection.Count
        If s <= 6 Then
            cht.SeriesCollection(s).Format.Fill.ForeColor.RGB = pal(s)
            cht.SeriesCollection(s).Format.Line.ForeColor.RGB = pal(s)
        End If
    Next s

    ' Pie/Doughnut charts have ONE series with multiple POINTS -- each point
    ' (category) must be colored individually, or every slice looks the same.
    If cht.ChartType = xlDoughnut Or cht.ChartType = xlPie _
       Or cht.ChartType = xlPieExploded Or cht.ChartType = xlDoughnutExploded Then
        Dim pIdx As Integer
        If cht.SeriesCollection.Count >= 1 Then
            For pIdx = 1 To cht.SeriesCollection(1).Points.Count
                cht.SeriesCollection(1).Points(pIdx).Format.Fill.ForeColor.RGB = _
                    pal(((pIdx - 1) Mod 6) + 1)
            Next pIdx
        End If
    End If

    ' Tighten bar/column charts so they look fuller, not sparse
    Select Case cht.ChartType
        Case xlBarClustered, xlColumnClustered
            cht.ChartGroups(1).GapWidth = 60
            cht.ChartGroups(1).Overlap = -10
        Case xlColumnStacked, xlBarStacked, xlAreaStacked
            cht.ChartGroups(1).GapWidth = 50
    End Select

    cht.Axes(xlValue).MajorGridlines.Border.Color = C(225, 228, 237)
    cht.Axes(xlValue).MajorGridlines.Border.Weight = xlHairline
    cht.Axes(xlCategory).MajorGridlines.Delete
    cht.Axes(xlValue).TickLabels.Font.Size = 7
    cht.Axes(xlCategory).TickLabels.Font.Size = 7
    cht.Axes(xlValue).TickLabels.Font.Color = C(80, 90, 110)
    cht.Axes(xlCategory).TickLabels.Font.Color = C(80, 90, 110)

    cht.ShowReportFilterFieldButtons = False
    cht.ShowLegendFieldButtons = False
    cht.ShowAxisFieldButtons = False
    cht.ShowValueFieldButtons = False

    cht.HasLegend = True
    cht.Legend.Font.Size = 7
    cht.Legend.Font.Color = C(55, 71, 79)
    cht.Legend.Position = xlLegendPositionBottom
    On Error GoTo 0
End Sub

Private Sub BuildSmallCharts(wsDash As Worksheet, wsPivot As Worksheet)
    MakeChart wsDash, wsPivot, "Inventory Flow", "PT_InventoryFlow", xlColumnClustered, 61, 1, 79, 13
    MakeChart wsDash, wsPivot, "Revenue vs Expenses", "PT_SalesTrend", xlColumnStacked, 61, 14, 79, 24

    On Error Resume Next
    Dim co As ChartObject
    For Each co In wsDash.ChartObjects
        If co.Name = "Cht_PT_InventoryFlow" Then
            If co.Chart.SeriesCollection.Count >= 2 Then
                With co.Chart.SeriesCollection(2)
                    .ChartType = xlLineMarkers
                    .AxisGroup = xlSecondary
                    .Format.Line.ForeColor.RGB = C(249, 168, 37)
                    .Format.Line.Weight = 2.25
                    .MarkerStyle = xlMarkerStyleCircle
                    .MarkerSize = 6
                    .MarkerForegroundColor = C(249, 168, 37)
                    .MarkerBackgroundColor = RGB(255, 255, 255)
                End With
            End If
            Exit For
        End If
    Next co
    On Error GoTo 0
End Sub

' =============================================================================
'  BUILD SLICERS
'  FIX v7: height is now based on the ACTUAL number of items in each field
'  (header ~24px + ~17px per item, capped to a sane range) so there's no
'  empty dead space below short lists. Uses the genuine built-in slicer
'  style "SlicerStyleDark3" (a real Excel built-in name) for strong navy/
'  dark contrast against our panel -- no fabricated SlicerStyle API used.
' =============================================================================
Private Sub BuildSlicers(wb As Workbook, wsDash As Worksheet, wsPivot As Worksheet)
    On Error GoTo SlicerErr

    With wsDash.Range("Y2:AB2")
        .Merge
        .Value = "  SMART FILTERS"
        .Interior.Color = C(13, 27, 42)
        .Font.Name = "Calibri": .Font.Size = 9
        .Font.Bold = True: .Font.Color = RGB(255, 255, 255)
        .HorizontalAlignment = xlHAlignLeft
        .VerticalAlignment = xlVAlignCenter
    End With
    With wsDash.Range("Y3:AB3")
        .Interior.Color = C(0, 137, 123)
    End With

    ' "SlicerStyleDark3" is a real built-in slicer style (dark theme, good
    ' contrast against our navy panel). Using a verified built-in name avoids
    ' any custom-style API, which is not safely scriptable from VBA.
    Dim styleName As String
    styleName = "SlicerStyleDark3"

    Dim fields(0 To 3)  As String
    Dim ptNames(0 To 5) As String
    fields(0) = COL_MONTH: fields(1) = COL_CATEGORY
    fields(2) = COL_SUPPLIER: fields(3) = COL_PRODUCT
    ptNames(0) = "PT_SalesTrend": ptNames(1) = "PT_CategoryAnalysis"
    ptNames(2) = "PT_ProductRanking": ptNames(3) = "PT_SupplierPerformance"
    ptNames(4) = "PT_InventoryFlow": ptNames(5) = "PT_Profitability"

    Dim slLeft  As Double: slLeft = wsDash.Cells(6, 25).Left + 4
    Dim slWidth As Double: slWidth = wsDash.Range(wsDash.Cells(6, 25), wsDash.Cells(6, 28)).Width - 8
    Dim panelTop As Double: panelTop = wsDash.Cells(6, 25).Top + 4
    Dim panelBottom As Double: panelBottom = wsDash.Cells(80, 25).Top   ' bottom limit of slicer panel
    Dim gap As Double: gap = 8

    Dim slTop As Double: slTop = panelTop
    Dim itemRowH As Double: itemRowH = 17
    Dim headerH As Double: headerH = 24

    Dim i As Integer
    For i = 0 To 3
        Dim sc As SlicerCache
        Set sc = Nothing
        On Error Resume Next
        Set sc = wb.SlicerCaches.Add2(wsPivot.PivotTables(ptNames(0)), fields(i))
        On Error GoTo 0

        If Not sc Is Nothing Then
            Dim itemCount As Long
            itemCount = 5
            On Error Resume Next
            itemCount = sc.SlicerCacheLevels(1).SlicerItems.Count
            On Error GoTo 0
            If itemCount < 1 Then itemCount = 1

            Dim slHeight As Double
            slHeight = headerH + (itemCount * itemRowH)
            If slHeight > 165 Then slHeight = 165
            If slHeight < 70 Then slHeight = 70

            If slTop + slHeight > panelBottom Then
                slHeight = panelBottom - slTop - 4
                If slHeight < 50 Then slHeight = 50
            End If

            Dim sl As Slicer
            Set sl = Nothing
            On Error Resume Next
            Set sl = sc.Slicers.Add(wsDash, , "SL_" & fields(i), fields(i), _
                                    slTop, slLeft, slWidth, slHeight)
            On Error GoTo 0

            If Not sl Is Nothing Then
                On Error Resume Next
                sl.Style = styleName
                If Err.Number <> 0 Then
                    Err.Clear
                    sl.Style = "SlicerStyleLight1"
                End If
                sl.NumberOfColumns = 1
                On Error GoTo 0
            End If

            Dim j As Integer
            For j = 1 To 5
                On Error Resume Next
                sc.PivotTables.AddPivotTable wsPivot.PivotTables(ptNames(j))
                On Error GoTo 0
            Next j

            slTop = slTop + slHeight + gap
        End If
    Next i
    Exit Sub
SlicerErr:
    Debug.Print "Slicer: " & Err.Description & " (continuing)"
End Sub

' =============================================================================
'  INSIGHTS PANEL
'  FIX: instead of hardcoded row ranges that broke when pivot sizes differed,
'  each formula now reads the LIVE label/value ranges directly from the
'  PivotTable object's RowRange / DataBodyRange, so it is always correct
'  no matter how many categories, products, suppliers or months exist.
' =============================================================================
Private Sub BuildInsights(wsDash As Worksheet, wsPivot As Worksheet)
    Dim pv As String: pv = wsPivot.Name

    With wsDash.Range("A51:X51")
        .Merge
        .Value = "   EXECUTIVE INSIGHTS  |  Key Performance Highlights"
        .Interior.Color = C(13, 27, 42)
        .Font.Name = "Calibri": .Font.Size = 9
        .Font.Bold = True: .Font.Color = RGB(255, 255, 255)
        .HorizontalAlignment = xlHAlignLeft
        .VerticalAlignment = xlVAlignCenter
    End With
    wsDash.Rows(51).RowHeight = 18

    Dim lbl(1 To 6) As String
    lbl(1) = "Highest Revenue Category"
    lbl(2) = "Best Performing Product"
    lbl(3) = "Top Supplier"
    lbl(4) = "Most Profitable Category"
    lbl(5) = "Best Revenue Month"
    lbl(6) = "Lowest Expense Month"

    Dim clr(1 To 6) As Long
    clr(1) = C(0, 137, 123): clr(2) = C(46, 125, 50)
    clr(3) = C(13, 27, 42): clr(4) = C(106, 27, 154)
    clr(5) = C(249, 168, 37): clr(6) = C(21, 101, 192)

    Dim icons(1 To 6) As String
    icons(1) = " REV ": icons(2) = " PRD ": icons(3) = " SUP "
    icons(4) = " PRO ": icons(5) = " MON ": icons(6) = " EXP "

    ' --- Compute insight values directly in VBA (100% reliable, no fragile
    '     range-address formulas that can silently fail to parse) ---
    Dim val(1 To 6) As String
    val(1) = TopLabelByValue(wsPivot, "PT_CategoryAnalysis", COL_CATEGORY, 1, True)
    val(2) = TopLabelByValue(wsPivot, "PT_ProductRanking", COL_PRODUCT, 1, True)
    val(3) = TopLabelByValue(wsPivot, "PT_SupplierPerformance", COL_SUPPLIER, 1, True)
    val(4) = TopLabelByValue(wsPivot, "PT_CategoryAnalysis", COL_CATEGORY, 2, True)
    val(5) = TopLabelByValue(wsPivot, "PT_SalesTrend", COL_MONTH, 1, True)
    val(6) = TopLabelByValue(wsPivot, "PT_SalesTrend", COL_MONTH, 3, False)


    Dim k As Integer
    For k = 1 To 6
        Dim ir As Long: ir = 51 + k
        wsDash.Rows(ir).RowHeight = 15

        With wsDash.Cells(ir, 1)
            .Value = icons(k)
            .Interior.Color = clr(k)
            .Font.Name = "Calibri": .Font.Size = 7
            .Font.Bold = True: .Font.Color = RGB(255, 255, 255)
            .HorizontalAlignment = xlHAlignCenter
            .VerticalAlignment = xlVAlignCenter
        End With

        With wsDash.Range(wsDash.Cells(ir, 2), wsDash.Cells(ir, 10))
            .Merge
            .Value = "  " & lbl(k)
            .Interior.Color = RGB(255, 255, 255)
            .Font.Name = "Calibri": .Font.Size = 8
            .Font.Bold = True: .Font.Color = clr(k)
            .HorizontalAlignment = xlHAlignLeft
            .VerticalAlignment = xlVAlignCenter
        End With

        With wsDash.Range(wsDash.Cells(ir, 11), wsDash.Cells(ir, 24))
            .Merge
            .NumberFormat = "General"
            .Value = val(k)
            .Interior.Color = C(244, 246, 251)
            .Font.Name = "Calibri": .Font.Size = 9
            .Font.Bold = True: .Font.Color = C(13, 27, 42)
            .HorizontalAlignment = xlHAlignLeft
            .VerticalAlignment = xlVAlignCenter
            .IndentLevel = 1
        End With

        With wsDash.Range(wsDash.Cells(ir, 1), wsDash.Cells(ir, 24)).Borders(xlEdgeBottom)
            .LineStyle = xlContinuous
            .Color = C(210, 218, 235)
            .Weight = xlHairline
        End With
    Next k

    BuildSmallCharts wsDash, wsPivot
End Sub

' Loops a pivot table's data rows directly (in VBA, not via formula) and
' returns the row label corresponding to the max (or min) value in the
' given data-field column. 100% reliable regardless of pivot size/shape.
'   ptName    = name of the pivot table
'   rowField  = name of the row field (used only for reference/clarity)
'   dataCol   = 1-based column index within the pivot's data area
'   findMax   = True for MAX, False for MIN
Private Function TopLabelByValue(wsPivot As Worksheet, ptName As String, _
                                  rowField As String, dataCol As Long, _
                                  findMax As Boolean) As String
    On Error GoTo Fail
    Dim pt As PivotTable: Set pt = wsPivot.PivotTables(ptName)

    Dim labelRng As Range: Set labelRng = pt.RowRange
    Dim dataRng  As Range: Set dataRng = pt.DataBodyRange

    If labelRng Is Nothing Or dataRng Is Nothing Then GoTo Fail
    If dataCol > dataRng.Columns.Count Then dataCol = 1

    Dim bestLabel As String, bestVal As Double, started As Boolean
    Dim r As Long, lblText As String, v As Variant

    ' RowRange includes the field header row; skip rows whose label is blank
    ' or matches a grand total caption.
    For r = 1 To labelRng.Rows.Count
        lblText = CStr(labelRng.Cells(r, 1).Value)
        If Len(Trim(lblText)) > 0 And InStr(1, lblText, "Grand Total", vbTextCompare) = 0 _
           And r <= dataRng.Rows.Count Then
            v = dataRng.Cells(r, dataCol).Value
            If IsNumeric(v) Then
                If Not started Then
                    bestVal = CDbl(v): bestLabel = lblText: started = True
                ElseIf (findMax And CDbl(v) > bestVal) Or (Not findMax And CDbl(v) < bestVal) Then
                    bestVal = CDbl(v): bestLabel = lblText
                End If
            End If
        End If
    Next r

    If started Then
        TopLabelByValue = bestLabel
    Else
        TopLabelByValue = "N/A"
    End If
    Exit Function
Fail:
    TopLabelByValue = "N/A"
End Function


' =============================================================================
'  FINAL POLISH
' =============================================================================
Private Sub FinalPolish(wsDash As Worksheet, wsPivot As Worksheet)
    On Error Resume Next
    ThisWorkbook.RefreshAll
    Application.Calculate
    On Error GoTo 0

    PopulateKPIValues wsDash

    wsDash.Columns(25).Font.Color = C(244, 246, 251)
    wsDash.Columns(25).Interior.Color = C(244, 246, 251)

    wsDash.Activate
    ActiveWindow.DisplayGridlines = False
    ActiveWindow.Zoom = 75

    wsDash.Range("A5").Select
    ActiveWindow.FreezePanes = True

    wsDash.Tab.Color = C(13, 27, 42)
    wsPivot.Tab.Color = C(0, 137, 123)

    wsPivot.Activate
    wsPivot.Columns.AutoFit

    wsDash.Activate
    wsDash.Range("A1").Select
End Sub

' =============================================================================
'  GENERIC HELPERS
' =============================================================================
Public Function GetLastRow(ws As Worksheet, col As Long) As Long
    GetLastRow = ws.Cells(ws.Rows.Count, col).End(xlUp).Row
End Function

Public Function GetLastCol(ws As Worksheet, rowNum As Long) As Long
    GetLastCol = ws.Cells(rowNum, ws.Columns.Count).End(xlToLeft).Column
End Function

Public Function GetWS(wsName As String) As Worksheet
    On Error Resume Next
    Set GetWS = ThisWorkbook.Worksheets(wsName)
    On Error GoTo 0
End Function

Dashboard Download(without VBA)


Trending Dashboads

Final Thoughts

The Inventory Intelligence Center is more than just a dashboard.

It is a decision-making system built inside Excel.

By combining automated KPIs, dynamic charts, supplier analytics, inventory flow monitoring, executive insights, and interactive filtering, it gives businesses the visibility they need to make smarter decisions and improve performance.

Whether you’re managing a warehouse, retail operation, distribution business, or manufacturing company, one thing is clear:

Better inventory intelligence leads to better business outcomes.

Comments

No comments yet. Why don’t you start the discussion?

    Leave a Reply

    Your email address will not be published. Required fields are marked *