1. Lighthouse

Level 9 · Boss: the app channel's year-over-year report

The request

From Andrew Wallace, Head of Mobile

“I'm asking for budget to rebuild the app's checkout, and the CFO wants proof the app is growing, not just having a good Black Friday. For each month of 2025, app sales only: last year's sales, the monthly growth, and the year-to-date growth so far.”

Your goal

Step 1 of 3. Create a measure called Sales LY: Sales for the same period last year, with CALCULATE and SAMEPERIODLASTYEAR('Date'[Date]).

  • Fill [Sales LY] on every row of the visualnot done yet
  • The Total row right toonot done yet

Step 1 · your measure

DAX
Visual
A matrix: rows = 'Date'[Month]; values = [Sales], [Sales LY]
Slicers
'Date'[Year] = 2025orders[channel] = app
Measures in the report
Sales = CALCULATE(SUM(order_items[line_total]), orders[status] = "completed")
Data pane: tables and columns
  • order_items order_item_id, order_id, product_id, quantity, unit_price, discount_pct, line_total
  • orders order_id, customer_id, order_date, status, channel, shipping_fee, coupon_code
  • products product_id, name, category, subcategory, unit_price, unit_cost, launched_on, is_active
  • customers customer_id, first_name, last_name, email, city, state, signup_date, segment, referral_source
  • returns return_id, order_item_id, returned_on, reason, refund_amount
  • Date Date, Year, Quarter, Month Number, Month, Year Month, Year Quarter, Day, Weekday Number, Weekday

Loading the editor…

    Visual[Sales LY] fills in when you press Check
    The visual before your answer
    MonthSalesSales LY
    Jan$38,566
    Feb$46,574
    Mar$80,375
    Apr$91,823
    May$94,842
    Jun$97,194
    Jul$77,398
    Aug$71,746
    Sep$67,690
    Oct$80,288
    Nov$118,602
    Dec$130,426
    Total$995,524

    Mote checks your result

    Press Check. Mote fills in your answer and compares it with what the report needs.

    Step 2 · your measure

    DAX
    Visual
    A matrix: rows = 'Date'[Month]; values = [Sales], [Sales YoY %]
    Slicers
    'Date'[Year] = 2025orders[channel] = app
    Measures in the report
    Sales = CALCULATE(SUM(order_items[line_total]), orders[status] = "completed")
    Data pane: tables and columns
    • order_items order_item_id, order_id, product_id, quantity, unit_price, discount_pct, line_total
    • orders order_id, customer_id, order_date, status, channel, shipping_fee, coupon_code
    • products product_id, name, category, subcategory, unit_price, unit_cost, launched_on, is_active
    • customers customer_id, first_name, last_name, email, city, state, signup_date, segment, referral_source
    • returns return_id, order_item_id, returned_on, reason, refund_amount
    • Date Date, Year, Quarter, Month Number, Month, Year Month, Year Quarter, Day, Weekday Number, Weekday

    Loading the editor…

      Visual[Sales YoY %] fills in when you press Check
      The visual before your answer
      MonthSalesSales YoY %
      Jan$38,566
      Feb$46,574
      Mar$80,375
      Apr$91,823
      May$94,842
      Jun$97,194
      Jul$77,398
      Aug$71,746
      Sep$67,690
      Oct$80,288
      Nov$118,602
      Dec$130,426
      Total$995,524

      Mote checks your result

      Press Check. Mote fills in your answer and compares it with what the report needs.

      Step 3 · your measure

      DAX
      Visual
      A matrix: rows = 'Date'[Month]; values = [Sales], [YTD Growth %]
      Slicers
      'Date'[Year] = 2025orders[channel] = app
      Measures in the report
      Sales = CALCULATE(SUM(order_items[line_total]), orders[status] = "completed")
      Data pane: tables and columns
      • order_items order_item_id, order_id, product_id, quantity, unit_price, discount_pct, line_total
      • orders order_id, customer_id, order_date, status, channel, shipping_fee, coupon_code
      • products product_id, name, category, subcategory, unit_price, unit_cost, launched_on, is_active
      • customers customer_id, first_name, last_name, email, city, state, signup_date, segment, referral_source
      • returns return_id, order_item_id, returned_on, reason, refund_amount
      • Date Date, Year, Quarter, Month Number, Month, Year Month, Year Quarter, Day, Weekday Number, Weekday

      Loading the editor…

        Visual[YTD Growth %] fills in when you press Check
        The visual before your answer
        MonthSalesYTD Growth %
        Jan$38,566
        Feb$46,574
        Mar$80,375
        Apr$91,823
        May$94,842
        Jun$97,194
        Jul$77,398
        Aug$71,746
        Sep$67,690
        Oct$80,288
        Nov$118,602
        Dec$130,426
        Total$995,524

        Mote checks your result

        Press Check. Mote fills in your answer and compares it with what the report needs.

        The lesson

        Andrew needs a growth story that survives a skeptical CFO. That takes three columns: what last year looked like, how each month compares, and where the year stands so far. Each is one of this chapter’s patterns, and each builds on the one before.

        What you are working in

        This page runs a practice DAX engine built for this site. Each step evaluates your newest measure in the matrix (months on the rows, 2025 and the app channel from the slicers) and checks every cell. Keep earlier measures in the editor: later ones use them. In Power BI Desktop, create each with Home → New measure, format the two growth measures as percentages, and add the two slicers.

        Step 1: last year, same period

        Sales LY = CALCULATE([Sales], SAMEPERIODLASTYEAR('Date'[Date]))

        The calendar filters (the Month row, the 2025 slicer) are replaced by last year’s dates, because the Date table is marked. The channel slicer is on orders, a different table, so it stays: the measure compares app with app.

        Step 2: monthly growth

        Sales YoY % =
        VAR ly = [Sales LY]
        RETURN
            DIVIDE([Sales] - ly, ly)

        The variable holds last year’s value once; DIVIDE protects the months where last year is blank.

        Step 3: year-to-date growth

        A single month can mislead: one strong promotion last year makes this year’s month look weak. The year-to-date comparison smooths that out. It needs two running totals, so two variables:

        YTD Growth % =
        VAR ytd = TOTALYTD([Sales], 'Date'[Date])
        VAR ytdLY = CALCULATE([Sales], DATESYTD(SAMEPERIODLASTYEAR('Date'[Date])))
        RETURN
            DIVIDE(ytd - ytdLY, ytdLY)

        ytdLY nests the functions: shift the visible month back a year, then extend it to January 1. Writing them as two separate CALCULATE filters would intersect 2025 dates with 2024 dates and return a blank.

        Reading the report

        The monthly column has one red cell: November, −7.1%. Taken alone it would sink the budget request. The YTD column answers it: the app was ahead of 2024 at every point of the year and finished 53.1% up. Present the two side by side, monthly growth for where things changed, YTD growth for where the year stands, and the November dip becomes a footnote.

        Common mistakes

        • Putting the channel filter inside the measures. It works for this page but makes every measure app-only. Leave it to the slicer so the same measures serve web and marketplace.
        • Growth on the Total row of the monthly column. It compares whole years; it is not an average of the twelve monthly rates.
        • Hard-coded years. 'Date'[Year] = 2024 instead of SAMEPERIODLASTYEAR breaks when the slicer moves to 2024.

        Pick what may run on this site. You can change this any time from "Cookie settings" at the bottom of every page.