Vojislav Vujić

Business planning showcase · synthetic data

Sales are growing. Is profit keeping up?

Orahovo Furniture Group is a fictional regional furniture manufacturer with entities in Serbia, Croatia and Bosnia and Herzegovina, four product lines and five years of monthly actuals and budgets. Every figure is generated from a fixed seed (20260908), so anyone can regenerate it and confirm none of it is real. I use it to show how I work, one stage at a time.

Data

Proves: data engineering, governance

Everything on this page is computed from one synthetic dataset. It is generated from a fixed seed, so anyone can regenerate it and confirm that none of it comes from a real company. This section describes what the dataset is and how it was made; the planning, forecast and explanation stages below read it through the API.

Plain words

This is where the numbers come from. Three companies keep their books a little differently, so the first job is to read all three and put them into one shape. Then I add up what was loaded and compare it with totals written down before loading. If both agree to the cent, the rest of the site can be trusted.

What it is

Loading the dataset description…

Why it is provably synthetic

Every figure is generated from seed 20260908, with the economic story written into the generator: an energy cost spike in 2022–23, price increases in 2023 and 2025, a supply disruption in Bosnia in mid-2024, a Croatian sales campaign in late 2025, and wage indexation every January. Budgets are built each year from the previous year's actuals with fixed uplifts.

What the raw extracts get wrong

The generator also writes deliberately dirty source files: three ledgers in three formats, duplicate postings, blank cost centres, a renamed cost centre, Croatian headers, comma decimals, a currency switch from kuna to euro, zero-padded account codes and a wide budget file. A loader rebuilds the clean schema from those files and reconciles it to control totals when the site is built.

This is an overview of the dataset. The reconciliation runs during the build; it is not an interactive view on this page.

Plan

Proves: controlling, budgeting, financial reporting

Pick an entity and a month. The P&L shows budget, actual and variance; the bridge walks from budget to actual through volume, price, unit cost, fixed cost and FX and must reconcile exactly; the cost-centre view shows FTE by cost centre next to the fixed-cost lines; the group view adds the intercompany elimination and a per-entity table. Everything on this panel comes from the API, nothing is typed into the page.

Plain words

A budget is a promise about the year; the actual is what happened. The bridge splits the difference into reasons: we sold more or fewer pieces, at higher or lower prices, each piece cost more or less to make, the fixed bills changed, and the exchange rate moved. The reasons add up to the whole difference, which is how you know nothing is missing.

Loading from /api/company/…

Forecast

Proves: financial forecasting

Monthly group revenue since 2022 with the last six months held out. Three models are fitted on the training window only and compared on that holdout: seasonal naive, seasonal with growth, and Holt-Winters. The benchmark must be beaten to justify the model. The best model's next six months, plus the last three months' prices, unit costs and fixed cost, form the baseline that the sliders below adjust.

Plain words

A forecast is a guess with a method. The simplest guess is the same month last year, and a model must beat that to earn its place. I hide the last six months, let each method guess them, and measure which came closest. The sliders then show what happens to the next half-year if volume, price or cost move.

Loading from /api/forecast

Explain

Proves: LLM orchestration

Ask why the result is below budget. The numbers are computed in Python; the model only puts them into words in one bounded call, and the answer is checked against those numbers before anything is shown. Every step appears in the trace with its status and timing, including answers that were rejected. It works with and without a model key; the template mode is always available and is labelled as such.

Plain words

The computer works out the numbers; the language model only puts them into sentences. Before you see an answer, every number in it is checked against the list of allowed numbers. If the model made one up, the answer is rejected and you can see that it was. A plain written template is always there as the fallback.

Shared with the Plan stage: changing one changes the other.

Usage: loading from /api/explain/usage

How this stage is built

Validate the request, compute every number in code, make one bounded model call with those numbers and a JSON contract, check that nothing in the answer is outside the fact pack, then present it with its trace. A rejected answer is shown as rejected, not hidden, and the deterministic template takes its place. The only paid call is this one, capped per day and cached per question.

Teach

Proves: training and coaching

The Plain words toggle in the header adds a short explanation under every stage, written for someone who has never read a variance bridge. Below is the exercise I would give a planning team on these numbers. Teaching is shown here as a skill, not sold as a service.

Plain words

Every stage above now has a panel like this one. Read the panel first, then the numbers. When the numbers make sense without the panel, switch it off.

Exercise: change a driver, read the bridge, explain what moved in one sentence.

Go to the Forecast stage, move only the Volume slider to +10%, and read the bridge from baseline to scenario. Write one sentence that says what moved and why, using the words volume, price, unit cost and fixed cost.

Reveal the model answer and the questions for the planning conversation

Model answer. Only the volume effect moves: the extra units earn the baseline contribution per unit, price and unit cost per piece are unchanged, and fixed cost does not react to volume, so the operating result improves by exactly the volume effect.

Four questions for the planning conversation.

  1. Which of the five effects did we decide, and which happened to us?
  2. Is the price effect a real price change, or a mix shift towards dearer products?
  3. Will the unit-cost effect repeat next month, or was it a one-off?
  4. Which budget assumption would have to change to close the gap, and who owns it?

That is how I structure teaching: one explanation in plain words, one exercise on the real numbers, then the questions that turn it into a discussion.

Agents

Every job I run reports back here when it finishes. The board reads /api/status; jobs post to /api/report with a token.

How jobs report to this board

A scheduled script recomputes the forecast and posts its status and metrics to the board's reporting endpoint with a token. Board state lives in the container's file system and resets on every redeploy; entries return as each job next runs.