Demand forecast
Every morning a chef answers the same question: how much of what to prep. The buyer answers the neighbouring one: how much of what to order. Usually both answer from memory.
Open Admin Panel → Inventory → "Forecast" tab (admin.cenaly.ge/inventory). The tab takes your sales history and turns it into three concrete artefacts: a prep plan, a supplier order draft, and a list of what risks running out before the day ends. If nothing is at risk, that block is not shown at all.
How the forecast is calculated#
Exactly two statistics, both explainable to a chef out loud:
- How much of this dish usually sells on this weekday. The history window is 8 weeks. The point estimate is the median, not the mean: eight Tuesdays, one of which was a 40-person birthday, tell you through the mean to "prep for 40" every week — the median ignores that Tuesday. Next to the number you get the honest observed range (min–max) and the number of observations.
- How that demand is spread across the day — shares by daypart: morning (6–11), lunch (11–16), evening (16–23), night (23–6). The boundaries are the same for every venue.
Fewer than three observations of a weekday means no number. The tab shows "too few" instead. A forecast you cannot trust is worse than no forecast: people prep and buy against it.
The selector at the top chooses the day to forecast — today or tomorrow.
The prep plan#
The dish forecast is exploded through recipes into a need for products and semi-finished items. The explosion uses the current recipes, at the moment you open the tab — if the chef changed a recipe this morning, the plan reflects it.
Nested semi-finished products are exploded fully: if a sauce is made from stock and the stock is itself a semi-finished item, both appear in the plan — otherwise a cook would be told to make the sauce without being told there is no stock for it.
The plan table:
| Column | What it means |
|---|---|
| Product | the semi-finished item to produce |
| Current qty | how much you already have |
| Needed | gross demand for the day from the forecast |
| To produce | the shortfall — what actually has to be cooked/prepped |
| By | the hour stock stops covering demand |
The deadline is not "start of shift". If the sauce covers the morning but not lunch, the cook has to be ready by 11:00, not by 6:00. Rows are sorted by deadline: the earliest first.
Semi-finished stock is spent once: if the same stock goes into both a sauce and a soup, a naive calculation would subtract it twice and the purchase order would come up short.
You can adjust the plan before sending it. Type your own quantity in the "To produce" column — for example, the chef knows about a banquet that is not in the sales history. Zero drops the row. The adjustment applies to the selected date and warehouse and resets when you change them; it goes to the printout, the shift board and "Record output".
The "Print" button opens a kitchen task sheet: the semi-finished item, how much to produce, "Ingredients for the batch" (the item's recipe multiplied by the quantity) and an empty "Done" column for ticks. If no window opens, the browser blocked pop-ups — allow them for this site.
The "Record output" button in a row opens the "Prep Production" operation with the planned quantity: it deducts raw materials by the item's recipe and adds to its stock.
The "Send to shift board" button turns the plan into a checklist of tasks — see Shift tasks board.
Dishes without a recipe do not enter the plan and are listed in a separate warning: the forecast can say nothing about them, and saying so out loud beats pretending the order is complete.
Prep for a date from placed orders#
Task: orders for tomorrow are already placed for a time — pickup at 14:00, delivery for lunch, a banquet "serve at 19:00" — and the kitchen needs to know in advance what each station has to prep, what guests asked for and what goes to which table. The forecast does not help here: it models a typical day, while these orders are known for sure.
- Open Inventory → Forecast — the "📅 Prep for a date" block sits under the prep plan. It works without a forecast too: for a new venue, or where there is no forecast, the tab shows just this block.
- Pick the date (tomorrow by default).
- Check the three parts of the block and press "🖨 Print" — everything goes on one sheet.
What is counted. The venue's orders whose serving time falls on the selected day: pickup and delivery for a time, a table order "serve at …" from the POS. Cancelled and closed orders and abandoned storefront carts are not included; a pre-order placed by a guest but not yet accepted is included — the kitchen plans for it too. "As soon as possible" orders without a time are not shown here.
By station. Dishes are split by kitchen screen station — the way the kitchen sees them: a station takes the menu categories ticked in its preset (kitchen screen → ⚙ → "Device presets", see Kitchen display). A category without a station goes to the fallback "other" station, and if there is none — to the "No station" row (a hint to finish the presets). An "all stations" preset without categories is an expo screen and gets no prep. Each station lists its dishes with quantities and the recipe ingredients: recipe rows × portions. A semi-finished product is marked "semi-finished" — the station takes it ready-made, so it has to be prepared in advance.
A combo is broken into its dishes (the burger to the hot line, the salad to the cold one), and so are the guest's combo slot choices; add-ons count by their own recipes. A recipe row limited to takeaway (box, container) only goes into pickup and delivery orders. Dishes without a recipe are listed in a warning: no prep is counted for them.
Raw goods in total. The total for all stations, with semi-finished products broken down to raw goods by their own recipe: 3 patties of 150 g beef each are 0.45 kg of beef. Quantities are gross — stock is not subtracted (what to buy given stock is shown by the purchase draft below or a supplier order).
Notes. Item comments ("no onion") and whole-order comments — a list by time, with the order number and table.
By table. Orders grouped by place: dining-room tables (by number), then pickup and delivery. Each order shows the serving time, the guest, cutlery ("Cutlery: 2" / "No cutlery"), the comment, the dishes (combo items with an ↳ arrow) and requests for dishes. This is the banquet prep sheet: what to serve at which table and by what time.
Example. For tomorrow at 14:00: two "Burger + salad + fruit drink" combos (one takeaway, one at table 5) and a burger at the same table marked "no onion". The block shows: hot line — 3 burgers (3 patties, 3 buns, onion), cold line — 2 salads, fruit drink — "No station" (no recipe), one box — for the takeaway only; notes — "no onion" for the table 5 burger; by table — table 5 with two orders and the pickup.
The calculation uses the recipes in force now: if a new recipe version is scheduled for a future date, or a pour of a different volume is ordered, quantities follow the current composition.
Defrost list#
Task: know the evening before what to take out of the freezer, how much and when, so it is ready by the kitchen start.
- On the frozen product card (“Storage” — “Freezer (−18 °C)”) fill in “Defrost time” — how many hours it takes to thaw, e.g. pork shoulder — 16 h.
- On the “Forecast” tab pick the day (e.g. tomorrow) — the “🧊 Defrost list” block appears.
- Set “Kitchen starts at” (10:00 by default).
Result: one line per product — how much to defrost (the day’s forecast need) and when to start. Example: shoulder 4 kg, 16 h, kitchen tomorrow from 10:00 → “start today at 18:00”. If stock is below the need, the line says “only … in stock”. “Print” gives a sheet with a “Done” box.
The supplier order draft#
The second block of the tab is the purchase draft for a chosen coverage horizon ("Cover, days": usually lead time plus a buffer).
For each product two values are computed and the larger one wins:
- forecast need over the horizon minus current stock;
- top-up to the reorder point (par level / minimum quantity) — ordinary stock logic.
Why the larger and not one instead of the other: the reorder point is a safety buffer "so we don't run dry", and it knows nothing about tomorrow's double-booked Friday; while the forecast knows nothing about products that never appear in recipes (packaging, consumables) — for those the reorder point is the only reference. The row shows which of the two rules fired.
Quantities are rounded up to the pack size where one is set on the product. Checked rows turn into supplier orders with one button — from there they are ordinary purchases in Inventory.
"Runs out around 19:00"#
The third block is the run-out risk before the day ends. It compares current stock with the expected consumption over the remaining hours, computed from the daypart profile, and names the hour when stock hits zero.
There is a correction for today's pace: if you have already used more than usual by lunch, the expectation for the rest of the day rises proportionally. The factor is clamped to ×0.5…×2 — a correction for "busy today" is useful, a prediction from a single receipt is not. If there is nothing to measure the pace from, the tab says plainly that the usual weekday profile was used.
New venue: seed the forecast from a similar one#
A venue that has just opened has no history, so there is nothing to forecast from. To make the prep plan and purchasing work from day one, borrow the forecast of a similar venue.
- Open Inventory → "Forecast" and choose the new venue's warehouse.
- Expand "🌱 Seed the forecast from a similar venue" (for a venue without history it is already open).
- In "Donor venue", pick a similar venue — same menu and similar guest flow.
- In "Coefficient", set how demand differs from the donor: 1 — the same, 0.7 — 30 % fewer guests (smaller hall, quieter street), 1.2 — 20 % more.
- Click "Save". The forecast, prep plan and purchase draft are recalculated from the donor at once.
For example, the donor sells 20 portions of soup on Mondays and the coefficient is 0.7 — the new venue sees a forecast of 14. Numbers taken from the donor are marked "🌱 seed".
Seeding fades on its own: once the new venue has three of its own observations for a dish on that weekday, its own median is used. The donor only feeds the forecast — its sales do not enter sales reports, analytics or forecast accuracy. To remove seeding, click "Remove seeding" in the same block.
Forecast accuracy#
The "🎯 Forecast accuracy" block at the bottom of the tab shows how far the forecast was off over the last 4 weeks. The heading shows the deviation for 4 weeks and for the last week, e.g. "deviation 18 % over 4 weeks · 15 % last week".
It has two tables:
- "Last week: forecast / actual / deviation" — for every working day: the sum of the forecast across dishes, how much of the same dishes was sold, the deviation and total sold (including new items).
- "Largest misses of the week" — up to 10 dishes where the forecast was furthest off.
A day's forecast is calculated as it looked that morning: the median of the same weekdays BEFORE that date. Deviation is the sum of per-dish misses divided by sales (WAPE): "we were off by 18 % of what we sold". Each dish is counted separately, so −10 soups and +10 salads do not make a "perfect" day. If the forecast is systematically too high or too low (by more than 5 %), a line says so — e.g. "On average the forecast is 12 % too high — preps are left over".
For example, on Tuesday the forecast was 40 portions across dishes, 46 were sold, and per-dish misses add up to 9 portions: the day's deviation is 9 / 46 ≈ 20 %. Dishes without a forecast (new items with fewer than three same weekdays) are not counted as misses.
Revenue and guests forecast for the week#
The "📈 Revenue and guests forecast for the week" block on the same tab answers the manager's question: what will the week look like — how much revenue, how many checks and guests to expect each day. "← Previous" and "Next →" flip through weeks, "This week" returns to the current one.
For each day you see:
- revenue — with a split by daypart below it (morning / lunch / evening / night); when events or an adjustment changed the number, a grey "base …" line shows the normal day;
- checks, guests and average check;
- the day's events and their impact;
- the manager's adjustment, if any.
The week total is at the bottom. It is an estimate, not a plan — and the block says so.
How it is calculated. The base is the same median of the same weekday over 8 weeks as for dishes, only for the venue's revenue, number of checks and guests. With fewer than three such days in history there is no number — "little history (2 of 3)" instead. Guests are the guest count from the till, or one guest for an order without it (takeaway, delivery). The daypart split follows how that weekday is usually distributed over the hours.
Day forecast = base × events × adjustment.
Events and promotions#
A holiday, a match next door, a promotion, a competitor opening nearby — add it in "📅 Events and promotions" with "+ Event": name, type, dates (past and upcoming), "Repeats every year", and which locations it applies to — this one or all.
An event's impact is measured on its past occurrences. For each past date of the event, actual revenue is divided by the "normal day" — the median of the same weekday over the 8 weeks before that date. The median of those ratios is the impact. For example, 8 March last year was +40 % over a normal Sunday — so this year's 8 March forecast is multiplied by 1.4. The event shows "past: +40 % (1)" — the number in brackets is how many occurrences it is based on: one occurrence is not yet a pattern.
If there are no past occurrences (a first match, a new promotion), enter your estimate in percent — "+30" or "−20". The "Use my estimate even when there is history" checkbox is for when last time was unusual. With no history and no estimate the event does not change the forecast and says "no impact yet".
"Add holidays" adds yearly holidays that most often move revenue (in the English interface: 1 January, 14 February, 8 March, 24, 25 and 31 December; in the Russian one — the Russian public holidays). Remove the ones you do not need in the event window.
Manager adjustment#
The manager knows what history does not: a banquet is booked, the street is dug up. "Adjust" in a day row opens a window: ± percent to the forecast or revenue for the day as a whole (checks and guests follow in the same proportion), plus a reason. The window shows the forecast without the adjustment. "Remove adjustment" returns to the calculated forecast.
Every adjustment, event and staff productivity change goes to the "Forecast change log" at the bottom of the block: when, who, what and why.
The same hourly forecast is used in the shift schedule: "Staff → Schedule" shows how many people each hour needs (see Shift schedule).
What the forecast needs#
| Needed | Why | Without it |
|---|---|---|
| Sales history | to take the weekday median | the tab says there isn't enough history yet |
| ≥ 3 of the same weekday in history | to have a sample at all | the dish shows "too few" |
| Dish recipes | to explode the forecast into products | the dish lands in the "no recipe" list |
| Up-to-date stock | to compute shortfall and risk | the plan proposes prepping everything from scratch |
The history aggregate is recalculated regularly on the server; recipe explosion and stock subtraction are computed when you open the tab, against current data.
Related sections#
- Inventory — recipes, stock, receiving and purchasing
- Shift tasks board — where the prep plan goes
- Menu engineering — what to do with a dish once you see both demand and margin
- Analytics — sales by dish and period
FAQ#
Why does the forecast say "too few" for half of the dishes?#
Because those dishes have fewer than three sales on this weekday in the history — for example, the dish went on the menu two weeks ago. The number appears by itself once there are enough observations.
Why the median and not the mean?#
The median is robust to outliers. One 40-person banquet shifts the mean enough to make the venue over-prep for weeks. The mean is in the data too — it shows how skewed the history is.
How do I know whether to trust the forecast?#
Open "🎯 Forecast accuracy": it shows the deviation over 4 weeks and a forecast/actual table for each day of last week. A 15–25 % per-dish deviation is normal for a small venue; if the block says the forecast is systematically too high, cut preps manually.
Does the forecast account for weather, holidays, football?#
The dish forecast does not: it knows only the weekday and the time of day. The weekly revenue and guests forecast accounts for the events you add in "📅 Events and promotions": the impact comes from their past occurrences or from your estimate. Neither accounts for weather.
Can I forecast a week ahead?#
Revenue, checks and guests — yes: the "📈 Revenue and guests forecast for the week" block flips through weeks. The dish forecast and prep plan cover today and tomorrow; for purchasing the horizon is the "Cover, days" field: the one-day forecast is multiplied by it.
Does the purchase draft send the order to the supplier straight away?#
No. It is a draft: you check rows, adjust quantities, and only then create the orders — ordinary purchases in the Inventory section.