The business reports

contoso includes 1,127 fictional internal business reports about Contoso’s trading from May 2016 to the end of 2025. They are the documents a finance team reads alongside its sales data: weekly flashes, monthly reviews, quarterly country reports, committee minutes and plan memos. Each is written by a named person in a named business unit, published on a realistic date, and based on the “mega” dataset from create_contoso_duckdb("mega").

reports <- read_reports()

Types of report

Business area Report type Authors (in post on the publication date) How often A full year 2016-2025 Typical length
Sales sales_flash: Weekly Sales Flash Sales Operations: Benjamin Castellanos to 2019, Priya Raman from 2020 Weekly, every Monday 52 (53 in 2020) 501 430 words
Sales regional: Quarterly Business Review Country managers for the US, Germany, UK and Canada Quarterly 16 152 720 words
E-commerce and marketing ecommerce: E-commerce Performance Review Digital Commerce: Inès Duarte to 2021, Marcus Lindqvist from 2022 Monthly 12 115 640 words
Supply chain supply_chain: Vendor & Supply Review Supply Chain & Procurement: Victor Adeyemi to September 2020, Hannah Okafor from October 2020 Monthly 12 115 680 words
Manufacturing manufacturing: Contoso, Ltd Plant Report Tomasz Wisniewski, private-label plant Monthly 12 115 580 words
Pricing pricing: Pricing & Promotions Committee Minutes Chair: Robert Ashdown to 2022, Olivia Grant from 2023 5 meetings a year 5 48 690 words
Planning and finance aop: Annual Operating Plan memo Corporate Planning: Margaret Holloway to June 2020, Daniel Okoye from July 2020 Once, before each year from 2017 1 9 1,040 words
Operations alert: Supply alerts and operations notices Supply Chain, Retail Operations When something happens 2-5 34 310 words
External market industry: Sector Review Northbridge Retail Insights, an outside analyst Quarterly 4 38 690 words

2016 is a partial year, because the data starts on 18 May 2016: weekly flashes run from week 21, monthly reports from June, quarterly reports cover Q3 and Q4 only, and there is no plan or budget.

reports |>
  dplyr::count(report_type, unit, sort = TRUE)
#> # A tibble: 13 × 3
#>    report_type   unit                                       n
#>    <chr>         <chr>                                  <int>
#>  1 sales_flash   Sales Operations                         501
#>  2 ecommerce     Digital Commerce                         115
#>  3 manufacturing Contoso Manufacturing (Private Label)    115
#>  4 supply_chain  Supply Chain & Procurement               115
#>  5 pricing       Pricing & Promotions Committee            48
#>  6 industry      Northbridge Retail Insights (external)    38
#>  7 regional      Regional Sales - Canada                   38
#>  8 regional      Regional Sales - Germany                  38
#>  9 regional      Regional Sales - United Kingdom           38
#> 10 regional      Regional Sales - United States            38
#> 11 alert         Retail Operations                         26
#> 12 aop           Corporate Planning                         9
#> 13 alert         Supply Chain & Procurement                 8

Each type follows its own template:

Report type Sections
sales_flash Headline, key numbers table, Channel, Country, Category, Watch list
regional Executive summary, Performance vs last year and budget, Category highlights, Store operations, Customers and competitors, Priorities next quarter, Asks of HQ
ecommerce Headline, Channel mix, Online performance vs last year and budget, Online category performance, Site and marketing notes, Priorities for next month
supply_chain Summary, Vendor scorecard (red/amber/green by manufacturer), Issues and mitigations, Outlook
manufacturing Summary, Sell-through by category and product line, Cost and yield, Capacity and launches, Risks
pricing Attendees, Review of recent performance, Discussion, Decisions, Action items
aop Summary of targets, Key assumptions, Phasing through the year, Risks to the plan, Accountabilities by unit
alert What happened, Impact, Actions, Next update
industry Key takeaways, Demand by category, Channels, Pricing and promotions, Outlook

A sample report

The Weekly Sales Flash for week 20, in full:

sample <- dplyr::filter(reports, id == "sales-flash-2025-w20")
cat(sample$body)
#> # Weekly Sales Flash - Week 20 (12 May 2025 to 18 May 2025)
#> Priya Raman, Director of Sales Operations, Sales Operations | 19 May 2025
#> 
#> **Headline**
#> - Revenue was $590M, flat on week 19 (-0.6%) and down about 9% on last year. That is a run-rate of roughly $2.4B a month.
#> - Stores were up 2.5% YoY. Online was down about 17% and is still the whole story on the gap.
#> - Margin held at 56% and discounts at 5.9%, within policy. No promo leakage.
#> 
#> | Metric | Week 20 | WoW | YoY |
#> |---|---|---|---|
#> | Revenue | $590M | -0.6% | -9.3% |
#> | Online | $334M | -1.2% | -16.6% |
#> | Stores | $257M | +0.3% | +2.5% |
#> | Gross margin | 56.0% | | |
#> | Discount | 5.9% | | |
#> | Units | 2.39M | | |
#> 
#> **Channel**
#> - **Online** is still carrying the marketplace exit. Migration to contoso.com is running well short of plan, and the paid-search cut isn't helping. The WoW slip of about 1% is small, but there is no sign of it turning.
#> - **Stores** are flat WoW and positive YoY for another week. Store Next is doing the lifting.
#> 
#> **Country**
#> - **Wave 1 markets are strong YoY:** Germany +25%, Netherlands +27%, Italy +28% and France +11%. All were up or roughly flat WoW.
#> - **Wave 2:** Canada was +13% YoY. Australia was +9% YoY but -4.7% WoW, the weakest WoW of any market.
#> - **UK** was +3% YoY and +2.7% WoW as the Store Next rollout starts.
#> - **US** was down about 11% YoY at $111M. There is no Store Next until the July pilot.
#> 
#> **Category**
#> - **Cameras** were +28% YoY. The A. Datum creator line and Fabrikam vlogging cameras in the experience zones are doing what they were meant to.
#> - **Audio** was +23% and **Games** +16%, both off a small base, which fits the floor-space shift.
#> - **TV and Video** was down about a third YoY (-36%) and -4% WoW. The range reset is biting far harder than the -8% plan assumption.
#> - **Computers** were down 10% YoY at $209M. Refresh demand isn't showing up yet, against a plan that weights the Lumen 10 lift to the first half.
#> - **Home Appliances** were down 17% YoY. The Litware safety notice may still be dampening the small-appliance categories.
#> 
#> **Watch list**
#> - **Computers:** only about six weeks are left in H1 for the refresh to show. Escalate if it is still negative YoY by week 24.
#> - **TV and Video:** is the WoW decline stabilising, or is there more floor-space drag to come?
#> - **Australia:** check whether the WoW dip is a one-off or a post-conversion fade.
#> - **UK:** track the weekly lift as stores convert.
#> - **Online:** watch contoso.com conversion from former marketplace customers.

Dimensions of a report

Metadata

Every report carries the same eight metadata fields, plus its text:

Field Type Example
id text sales-flash-2025-w20
title text Weekly Sales Flash - Week 20 (12 May 2025 to 18 May 2025)
unit text Sales Operations
author text Priya Raman, Director of Sales Operations
report_type text sales_flash
period_start date 2025-05-12
period_end date 2025-05-18
published date 2025-05-19
body Markdown text the report itself

period_start and period_end give the period a report covers, and published gives the date it was released. The gap between them varies by type: a weekly flash comes out the day after its week ends, a regional review about 12 days after its quarter. The plan memo is negative: each one is published in December of the year before the one it covers.

Business dimensions

The figures in each report are cut by the same dimensions as the contoso data. Which cuts a report uses depends on its type:

Report type Period Breakdowns Compared with
sales_flash Week Total, channel (online / stores), country, category Prior week, same week last year
regional Quarter One country: total and category Prior quarter, last year, budget
ecommerce Month Channel mix, online total, online by category Prior month, last year, budget
supply_chain Month Total, manufacturer, category Prior month, last year, budget
manufacturing Month Contoso, Ltd products: total, category, product line Prior month, last year
pricing The 8 weeks before the meeting Total, category, channel Prior 8 weeks, last year
aop Full year, 2017-2025 Budget by channel, country, category and month The previous year’s actuals
alert One event Vendors, categories and regions affected None
industry Quarter Market-level demand by category and channel (no Contoso figures) Market last year

The values those breakdowns take:

Dimension Values
Channel Online, Stores
Country United States, Canada, United Kingdom, Germany, France, Italy, Netherlands, Australia, plus Online
Category Computers, Cell phones, Home Appliances, TV and Video, Cameras and camcorders, Audio, Games and Toys, Music, Movies and Audio Books
Manufacturer 11, including Contoso, Ltd, the company’s own private label

Measures

The numbers quoted in the reports are these measures:

Measure Meaning
Net revenue Sales after discounts, in USD
Change vs prior period Against the previous week, month or quarter
Change vs last year Against the same period a year earlier
Variance to budget Against that year’s Annual Operating Plan (from 2017)
Gross margin % Gross margin as a share of net revenue
Discount % Discounts as a share of gross revenue
Cost ratio Cost of goods as a share of gross revenue
Units Units sold

The years in the reports

Reports run from the first weekly flash in May 2016 to the last reviews of 2025, published in January 2026:

reports |>
  dplyr::count(year = format(published, "%Y"), name = "reports")
#> # A tibble: 11 × 2
#>    year  reports
#>    <chr>   <int>
#>  1 2016       60
#>  2 2017      117
#>  3 2018      119
#>  4 2019      117
#>  5 2020      119
#>  6 2021      118
#>  7 2022      117
#>  8 2023      117
#>  9 2024      118
#> 10 2025      117
#> 11 2026        8

Together they tell one continuous story, and every event explains a movement in the data:

Year What happens
2016 A new sales system goes live on 18 May, which is why the data starts then
2017 A laptop and smartphone boom and the last big wave of new customers; smartphones replace compact cameras
2018 A price-list reset, four store openings and a one-off anniversary sale
2019 The best year so far, driven by a new loyalty programme that makes existing customers buy more often
2020 A rush to redeem loyalty points, then a component shortage that cuts stock across the range
2021 The shortage at its worst, then recovery
2022 Marketplaces, a TV and air-conditioning boom and a strong dollar
2023 A record year
2024 A difficult year, as customers buy less often and the best ones start to leave
2025 A marketplace exit, a TV range reset, a store refit programme (“Store Next”), a product safety notice and a PC refresh

The stocktake, summer shutdown and holiday season recur every year. Each author only knows what their unit would know on the publication date, and people change roles over the years.

Glossary of terms

The terms the reports use, and the phrasings to search for, are defined in a glossary:

fp <- system.file("extdata", "reports", "glossary.yaml", package = "contoso")
glossary <- yaml12::read_yaml(fp)
glossary$win_back$definition
#> [1] "The marketing programme, run every year from 2018, that targets customers who have not bought for a year or more; also the customers who come back. Returners: about 104k (2018), 122k (2019), 47k (2020), 91k (2021), 104k (2022), 82k (2023, including 743 not seen since 2016), 28k (2024). Returners spend more than before they lapsed.\n"

Some reports are deliberately wrong. A few authors misread the data or credit the wrong cause, as real business writing does. The package does not say which, so an FP&A assistant has to find them by checking the reports against the data.