# Case: plan vs actual by price, volume and mix

> Price volume mix analysis: splitting a plan vs actual miss into price, volume, mix and currency.

- Course: [Finance for Product People: From Unit Economics to Business Cases](https://productovi.com/courses/finance-for-product.md)
- Module: What will next year look like?
- Lesson 28/32, ~42 min
- Source: https://productovi.com/courses/finance-for-product/plan-vs-actual-case

## Sales beat the plan

**Alex Tutor:** On Klasna's product team, September's results have just come in.

**Leo:** September is in: 3,709,960 UAH of sales against a plan of 3,647,800. We beat the plan! The back-to-school promo on English worked.

**Hanna:** Sales, yes. Contribution came in at 996,149.75 against 1,050,847. We're 54,697.25 short where it counts.

**Sofia:** How can we sell more and earn less?

**Alex Tutor:** Before we take it apart: which headline should the board read first?

> **Exercise:** September at Klasna: sales 3,709,960 UAH against a plan of 3,647,800; contribution margin 996,149.75 UAH against a plan of 1,050,847. Which headline belongs at the top of the board note?

**Alex Tutor:** That's what **variance analysis** is for: take the total difference apart into its causes. For Klasna there are four: how many lessons (volume), which lessons (mix), at what price, and at what exchange rates. Split this way, it's called **price volume mix analysis**.

**Dataset (CSV):** [September 2026: plan vs actual by subject and market](https://productovi.com/datasets/finance-for-product/plan-vs-actual-case/klasna-september-plan-actual.csv)

Families in Ukraine (ua) pay in UAH, families in the EU (eu) in EUR; prices are per lesson in that currency. Payment fee 2.5% of what families pay. Video + AI is billed in USD. Rates: plan EUR 45.00 and USD 42.00 UAH; actual September EUR 47.00 and USD 43.50 UAH.

- `subject`: english, maths or programming
- `market`: ua: families in Ukraine; eu: families in the EU
- `currency`: Currency the family pays in
- `plan_lessons`: Lessons in the September plan
- `actual_lessons`: Lessons actually delivered in September
- `plan_price_per_lesson`: Planned average price per lesson, in the row's currency
- `actual_price_per_lesson`: Actual average price per lesson, in the row's currency
- `payout_per_lesson_uah`: Tutor payout per lesson, UAH (as planned)
- `video_ai_usd_per_lesson`: Video + AI per lesson, USD

**Alex Tutor:** Start with volume. Klasna delivered 5,890 lessons against 5,612 planned. Value the extra ones at the plan's average contribution per lesson, 187.25: as if they were ordinary planned lessons.

> **Exercise:** What is September's **volume variance**: the extra lessons valued at the plan's average contribution of 187.25 UAH per lesson?

## Sales mix variance: which lessons

**Sofia:** So volume alone added 52,055.50. Where did it go?

**Alex Tutor:** First, which lessons came. At plan prices an English lesson in Ukraine leaves 168.13; every other row leaves more, from 195.50 to 240.50. September brought 454 more English lessons at home and 120 fewer programming ones.

**Hanna:** For the mix, value each row's actual lessons at that row's plan contribution, then subtract all actual lessons at the plan average. Here that's −11,557.50.

$$
\Delta CM = \underbrace{(L_a - L_p)\,\overline{cm}_p}_{\text{volume}} + \underbrace{\textstyle\sum L_{a,i}\,cm_{p,i} - L_a\,\overline{cm}_p}_{\text{mix}} + \underbrace{\textstyle\sum L_{a,i}(P_{a,i}-P_{p,i})\,R_p\,0.975}_{\text{price}} + \text{FX}
$$

*L: lessons (a: actual, p: plan, i: row); cm_p,i: a row's plan contribution per lesson; cm̄_p: the plan's average (187.25); P: price in the row's currency; R_p: plan rate (1 for UAH). FX: the same actual lessons and prices at actual rates minus plan rates, on sales and on the USD video bill.*

**Alex Tutor:** Same number of lessons, a cheaper shelf. The promo pulled families toward the row that leaves the least.

> **Exercise:** In September Klasna delivered 278 more lessons than planned (5,890 vs 5,612). By row: English in Ukraine +454, English in the EU +20, maths in Ukraine −66, maths in the EU −10, programming in Ukraine −100, programming in the EU −20. At plan prices English in Ukraine leaves 168.13 UAH per lesson; every other row leaves 195.50–240.50; the plan average is 187.25. Why is September's **mix variance** negative?

## Price variance: what the discount cost

**Alex Tutor:** Now price. Keep the plan rates, so currency doesn't sneak in: actual lessons × (actual price − plan price) × rate × 0.975, row by row.

**Sofia:** English at home went from 615 to 585: 3,640 × (−30) × 0.975 = −106,470. That's the promo.

**Hanna:** Programming had a smaller discount too: a partner school got a deal. Add it before you call it a total.

> **Exercise:** Use the dataset. What is the total **price variance** for September, at plan exchange rates and after the 2.5% fee? Enter a loss as a negative number.

## FX variance: the currency month

**Hanna:** And then the hryvnia weakened: the euro went from 45 to 47, the dollar from 42 to 43.50.

**Alex Tutor:** Two effects at once, in opposite directions. EU families paid the same euros, now worth 2 more hryvnias each, minus the fee. Video cost 1.50 more per dollar: 5,890 × 0.75 × 1.50 = 6,626.25 against us.

**Sofia:** The EU rows paid 12,180 euros in September. Let me turn that into hryvnias.

> **Exercise:** EU families paid 12,180 EUR for September's lessons. The euro was 47.00 UAH instead of the planned 45.00. How much did the currency move add to contribution on these sales, after the 2.5% fee?

**Hanna:** Net FX: +17,124.75. Without the weaker hryvnia, September would look 17 thousand worse. That isn't our merit, and next month it can turn.

**Alex Tutor:** Hanna's point in one number: what would September look like without the currency?

> **Exercise:** Klasna's September contribution bridge against plan: volume +52,055.50 UAH, mix −11,557.50, price −112,320, FX +17,124.75. What is the gap that comes from the business itself, **without** the currency effect? Enter a shortfall as a negative number.

**Alex Tutor:** Now line the pieces up the way the board will read them.

> **Exercise:** Put the steps of a contribution bridge from plan to actual in the usual order. In this order each step changes one thing and keeps the rest at plan.

**Alex Tutor:** Now check that the pieces add up: 52,055.50 − 11,557.50 − 112,320 + 17,124.75 = −54,697.25. The bridge closes exactly. The wider method: [budget vs actual variance analysis](/blog/budget-vs-actual-variance-analysis).

> **September 2026: the contribution bridge**
>
> | Step | UAH |
> |---|---|
> | Plan contribution | 1,050,847.00 |
> | Volume: 278 more lessons × 187.25 | +52,055.50 |
> | Mix: more English in Ukraine, less programming | −11,557.50 |
> | Price: English promo and a programming deal | −112,320.00 |
> | FX: EUR sales +23,751.00, USD video −6,626.25 | +17,124.75 |
> | **Actual contribution** | **996,149.75** |

## Did the promo pay for itself?

**Leo:** Fine, but the promo brought those extra lessons. Doesn't the volume pay for the discount?

**Alex Tutor:** Look only at the promo's row. It brought 454 extra English lessons in Ukraine. Even if every one of them came because of the promo, at plan prices they'd leave 454 × 168.13 = 76,328.75.

**Hanna:** And the 30-hryvnia discount went to all 3,640 lessons, including families who'd have come anyway.

**Alex Tutor:** Put Leo's claim to the test before anyone writes it down.

> **Exercise:** **True or false:** Klasna's back-to-school promo cut the price of English lessons in Ukraine from 615 to 585 UAH on all 3,640 lessons delivered (2.5% payment fee). It brought 454 extra lessons, each leaving 168.13 UAH at plan prices. Even if every extra lesson came because of the promo, it paid for itself.

**Sofia:** So the extra volume doesn't cover the discount. Let me put that in a reply to Leo.

> **Exercise:** Leo says: "The promo brought the extra lessons, so the volume paid for the discount." The facts: the promo cut English lessons in Ukraine from 615 to 585 UAH on all 3,640 lessons delivered (2.5% payment fee); it brought 454 extra lessons, each leaving 168.13 UAH at plan prices, the lowest of all rows. Answer him in 2–4 sentences with the numbers.

## The September note

**Leo:** The board gets a monthly note. I wanted to write "sales beat the plan". What should it say instead?

**Hanna:** The real result, the bridge, what was ours and what was the currency, and what we change for October.

**Alex Tutor:** And remember the forecast rolls: what September teaches you goes into the drivers for the coming months.

**Sofia:** Numbers first, then the decisions. Short enough for a board member to read in two minutes.

> **Exercise:** Write the September note for the board: what happened against plan, and what changes for October.

> **Key ideas**
>
> - Judge a month by contribution, not sales: September sold 62,160 UAH more than planned and earned 54,697.25 less.
> - **Variance analysis** splits the gap into causes that add up exactly: volume +52,055.50, mix −11,557.50, price −112,320, FX +17,124.75.
> - Value volume at the plan's average contribution; mix is what's left when lessons shift between rows with different margins.
> - Compute price at plan rates and FX separately. A weaker hryvnia raised EUR sales (+23,751) and the USD video bill (−6,626.25) at once; neither is the team's merit.
> - A discount pays only if the extra lessons it brings leave more than it gives away on all lessons: here 76,328.75 against 106,470, net −30,141.25 even in the best case.

**Alex Tutor:** That's the module in one month: a forecast built from drivers, the drivers that matter most, a budget made of honest drivers, and a variance that tells you which driver moved.

- Previous lesson: [Budgets people believe](https://productovi.com/courses/finance-for-product/budgets-people-believe.md)
- Next lesson: [Relevant, sunk and opportunity costs](https://productovi.com/courses/finance-for-product/relevant-and-sunk-costs.md)

---
Productovi Learning. Free, no sign-up needed to read.
