Excel Course
Excel Capstone Project
This is the final lesson of the course — and it is not a lesson. It is a brief. You have learned Power Query, Power Pivot, DAX, dashboards, charts, automation, and financial modelling across 49 lessons. Now you build something that uses all of it. The capstone is a complete, real-world workbook for a retail company called NorthBridge Trading Co. — end to end, from raw CSV imports to a polished three-dashboard report pack. No instructions are given step by step. The brief tells you what the business needs. You decide how to build it.
The Brief
Company: NorthBridge Trading Co. — a mid-sized UK retail business with 6 product categories, 4 regional sales teams, and 3 years of transactional history.
Deliverable: A single Excel workbook containing three dashboards — a Sales Performance Dashboard, an Inventory & Margin Dashboard, and a Regional Scorecard — all driven by a shared Power Pivot data model with DAX measures.
Users: The Sales Director (wants revenue, targets, and regional comparisons), the Operations Manager (wants margin, stock turn, and category performance), and the Board (wants one-page scorecard with YoY growth and headline KPIs).
Constraint: The workbook must be self-refreshing via Power Query, updateable by non-technical staff, and load in under 3 seconds on a standard business laptop.
The Dataset
The capstone uses four raw tables. Build them as named Excel Tables and load each through Power Query before connecting to Power Pivot. Do not type formulas directly into the raw sheets — all transformations happen in Power Query or as DAX measures.
Table 1: Sales_Raw (fact table — 72 rows, 3 years × 4 regions × 6 categories)
Columns: OrderID, OrderDate, Region, Category, Product,
UnitsSold, UnitPrice, UnitCost, TargetUnits, TargetRevenue
Sample rows:
OrderID OrderDate Region Category Product UnitsSold UnitPrice UnitCost TargetUnits TargetRevenue
1001 01/01/2023 North Electronics Smart Speaker 42 £89.99 £44.00 40 £3,600
1002 01/01/2023 South Clothing Winter Jacket 28 £64.99 £22.00 30 £1,950
1003 01/01/2023 East Homeware Coffee Maker 35 £49.99 £18.50 35 £1,750
1004 01/01/2023 West Grocery Organic Bundle 110 £18.99 £9.20 100 £1,900
1005 01/02/2023 North Beauty Skincare Set 22 £34.99 £12.00 25 £875
1006 01/02/2023 South Sports Yoga Mat Set 18 £44.99 £16.50 20 £900
...continues for 3 years (2023, 2024, 2025)
Table 2: DimDate (date dimension — same structure as Lessons 47–49)
Columns: Date, Year, Month, MonthNum, Quarter, MonthYear, FY_Quarter
FY_Quarter: NorthBridge FY runs Apr–Mar. Q1 = Apr–Jun, Q2 = Jul–Sep, etc.
Table 3: DimProduct (product lookup)
Columns: Product, Category, SubCategory, LaunchYear, IsActive, SupplierID
Table 4: Targets (monthly targets by region and category)
Columns: Year, Month, Region, Category, TargetRevenue, TargetUnits, TargetMargin
Power Query — Required Transformations
Every transformation below must be completed in Power Query before the data reaches Power Pivot. This keeps the data model clean and makes the workbook updateable without touching any formulas.
-- Sales_Raw query — required steps --
1. Load from Excel Table (or CSV if practising with external files)
2. Set data types explicitly:
OrderDate → Date
UnitsSold → Whole Number
UnitPrice → Currency (£)
UnitCost → Currency (£)
3. Add calculated columns in Power Query (NOT in Power Pivot):
Revenue = UnitsSold × UnitPrice
Cost = UnitsSold × UnitCost
-- Keep unit-level columns for DAX aggregation
4. Add a Year column: Date.Year([OrderDate])
Add a MonthNum column: Date.Month([OrderDate])
-- Used for relationship to DimDate
5. Filter: remove any rows where UnitsSold = 0 or null
-- These are cancelled orders — exclude from analysis
6. Rename columns to PascalCase for consistency:
"OrderID", "OrderDate", "Region", "Category", "Product",
"UnitsSold", "UnitPrice", "UnitCost", "Revenue", "Cost",
"TargetUnits", "TargetRevenue", "Year", "MonthNum"
-- Targets query — required steps --
1. Unpivot if targets arrive as a wide table (one column per month)
Select identifier columns → Transform → Unpivot Other Columns
Rename "Attribute" → "Month", "Value" → varies by metric
2. Add MonthNum: look up month number from a reference table
or use: = List.PositionOf({"January","February",...}, [Month]) + 1
3. Merge with DimDate on Year + MonthNum to add a proper Date column
-- Required for time intelligence measures to work correctly
The Data Model in Power Pivot
Year
MonthNum ←
Quarter
FY_Quarter
→ MonthNum
→ Region
→ Product
Revenue · Cost
Category
SubCategory
IsActive
→ Region
TargetRevenue
TargetMargin
DAX Measure Library — Build These First
Create all measures in a dedicated measures table named "NB Metrics" before building any PivotTable. Measures written before the dashboard are reusable, testable, and easier to debug.
-- === CORE SALES MEASURES === --
Total Revenue =
SUM(Sales_Raw[Revenue])
Total Cost =
SUM(Sales_Raw[Cost])
Gross Profit =
[Total Revenue] - [Total Cost]
Gross Margin % =
DIVIDE([Gross Profit], [Total Revenue], 0)
Units Sold =
SUM(Sales_Raw[UnitsSold])
Avg Order Value =
DIVIDE([Total Revenue], DISTINCTCOUNT(Sales_Raw[OrderID]), 0)
-- === TARGET & VARIANCE MEASURES === --
Target Revenue =
SUM(Targets[TargetRevenue])
Revenue vs Target =
[Total Revenue] - [Target Revenue]
Revenue vs Target % =
DIVIDE([Revenue vs Target], [Target Revenue], 0)
Target Units =
SUM(Targets[TargetUnits])
Units vs Target =
[Units Sold] - [Target Units]
Target Margin =
SUM(Targets[TargetMargin])
Margin vs Target =
[Gross Margin %] - [Target Margin]
-- === TIME INTELLIGENCE MEASURES === --
YTD Revenue =
CALCULATE(
[Total Revenue],
DATESYTD(DimDate[Date])
)
Prior Year Revenue =
CALCULATE(
[Total Revenue],
SAMEPERIODLASTYEAR(DimDate[Date])
)
YoY Growth % =
DIVIDE(
[Total Revenue] - [Prior Year Revenue],
[Prior Year Revenue],
0
)
YTD Prior Year =
CALCULATE(
[Total Revenue],
DATESYTD(SAMEPERIODLASTYEAR(DimDate[Date]))
)
Rolling 3M Revenue =
CALCULATE(
[Total Revenue],
DATESINPERIOD(DimDate[Date], LASTDATE(DimDate[Date]), -3, MONTH)
)
-- === INVENTORY & EFFICIENCY MEASURES === --
Revenue per Unit =
DIVIDE([Total Revenue], [Units Sold], 0)
Cost per Unit =
DIVIDE([Total Cost], [Units Sold], 0)
Contribution Margin =
[Revenue per Unit] - [Cost per Unit]
Category Share of Revenue =
DIVIDE(
[Total Revenue],
CALCULATE([Total Revenue], ALL(Sales_Raw[Category])),
0
)
Region Share of Revenue =
DIVIDE(
[Total Revenue],
CALCULATE([Total Revenue], ALL(Sales_Raw[Region])),
0
)
Dashboard 1 — Sales Performance Dashboard
Dashboard 2 — Inventory & Margin Dashboard
| Category | Revenue | Gross Profit | Margin % | Target | vs Target |
|---|---|---|---|---|---|
| Electronics | £804K | £377K | 46.9% | 45.0% | +1.9pp |
| Clothing | £483K | £212K | 43.9% | 42.0% | +1.9pp |
| Homeware | £398K | £159K | 40.0% | 39.0% | +1.0pp |
| Sports | £312K | £116K | 37.2% | 37.0% | +0.2pp |
| Grocery | £540K | £173K | 32.0% | 34.0% | -2.0pp |
| Beauty | £312K | £89K | 28.4% | 32.0% | -3.6pp |
Dashboard 3 — Regional Scorecard
Regional Scorecard — one row per region, six KPI columns
PivotTable setup:
Rows: Region (from Sales_Raw)
Values: Total Revenue · Revenue vs Target % · Gross Margin % ·
YoY Growth % · Units Sold · Units vs Target
Layout: Tabular · subtotals off · grand total row at bottom
Conditional formatting rules (apply column by column):
Revenue vs Target % → 3-colour scale: red (below 0%) / yellow (0%) / green (above 5%)
Gross Margin % → Icon set: green circle (≥40%) / yellow triangle (35–40%) / red diamond (<35%)
YoY Growth % → Data bars, green, midpoint at 0%
Units vs Target → Red if negative (below target), green if positive
Sort: Total Revenue descending so best-performing region appears first
GETPIVOTDATA for Board summary card (separate cell block above the scorecard):
Top region: =GETPIVOTDATA("Total Revenue", PT_Scorecard, "Region", INDEX(regions,MATCH(MAX(revenues),revenues,0)))
Fastest growing: =GETPIVOTDATA("YoY Growth %", PT_Scorecard, "Region", [region with max YoY])
Below target count: =COUNTIF(variance_range, "<0") -- number of regions missing revenue target
Workbook Structure & Final Checklist
Sheet order (tab colour coded):
[Green] Raw_Sales -- Power Query output, do not edit
[Green] Raw_Targets -- Power Query output, do not edit
[Green] DimDate -- date dimension, do not edit
[Green] DimProduct -- product lookup, do not edit
[Grey] PT_Helper -- all PivotTables, hidden before sharing
[Blue] Dashboard_Sales -- Dashboard 1
[Blue] Dashboard_Margin -- Dashboard 2
[Blue] Dashboard_Score -- Dashboard 3
[Yellow] Notes -- data dictionary, refresh instructions
Pre-delivery checklist:
☐ Power Query loads without errors on a clean open (Data → Refresh All)
☐ All slicers connected to all relevant PivotTables via Report Connections
☐ Conditional formatting applies correctly for each sign convention
Revenue vs Target: positive = green, negative = red
Cost vs Target: positive = red, negative = green
Margin vs Target: positive = green, negative = red
☐ KPI cards use GETPIVOTDATA (not hardcoded values)
☐ No formula errors (#REF!, #VALUE!, #DIV/0!) visible anywhere
☐ PT_Helper sheet hidden (right-click tab → Hide)
☐ Raw sheets protected (Review → Protect Sheet, allow no changes)
☐ Dashboard sheets protected (allow only slicer interaction)
☐ File saved as .xlsm if macros used, .xlsx if not
☐ File size under 5MB (check: File → Info → bottom of panel)
☐ Open on a colleague's machine and confirm slicers respond correctly
🔍 Practice
Q1. The Regional Scorecard shows East is below revenue target by -1.4% but above YoY growth at +6.8%. Write the one-sentence interpretation you would give the Sales Director — and identify whether this is a target-setting issue or a performance issue.
Q2. You need the YTD Revenue measure to reset on 1 April (NorthBridge's financial year start) rather than 1 January. Rewrite the YTD Revenue measure to use the FY start date.
Q3. The Beauty category shows a margin of 28.4% against a target of 32.0%. Write the conditional formatting rule — using the Margin vs Target measure — that colours the cell red, and explain why you use Margin vs Target rather than Margin % directly.
🟢 Quiz
Q1. The Rolling 3M Revenue measure uses DATESINPERIOD with -3, MONTH. A colleague suggests replacing it with DATESYTD for simplicity. What is wrong with that suggestion?
Q2. After protecting the Raw_Sales sheet, a colleague reports that Data → Refresh All no longer updates the Power Query data. What is the cause and fix?
Q3. The Board wants a single cell that always shows the name of the best-performing region by YoY Growth %. You plan to use GETPIVOTDATA to pull this. A colleague says just hardcode "North" since it has always been the top region. What is the professional argument against hardcoding?
Course Complete
You've completed the Excel course.
From your first SUM formula to a three-dashboard report pack with a shared Power Pivot data model — 50 lessons, one continuous goal: turning spreadsheets into decisions.
Lessons 1–15: Beginner — formulas, formatting, basic charts, and data entry
Lessons 16–30: Intermediate — VLOOKUP, PivotTables, conditional logic, and data validation
Lessons 31–40: Advanced — Power Query, Power Pivot, DAX, and automation
Lessons 41–50: Final Projects — real-world dashboards, financial modelling, and the capstone