How this demo was built
Every number you see comes from twelve months of operations at a simulated restaurant. Two things below: how that data was built, and what we do with it.
Part 1 — Where the data comes from
Why not just use a real restaurant’s books
We do have real books, and we do serve real restaurants. But they don’t work for a demo, for three reasons:
Real books are incomplete
Most restaurants only have monthly totals: revenue in, purchases out. No daily sales, no prep log, no itemised waste. Half the product would show "no data" on that.
Someone else’s numbers aren’t ours to show
Purchase prices, margin structure, supplier terms — that is a restaurant’s commercial position. Not something to put in front of third parties.
And the one that matters most: real books have no answer key
The braised beef sold out at 12:35. What was real demand that day? The till will never know — the people who wanted it turned around and left. You cannot check whether any forecast was right, because the thing to check against does not exist.
So we built a restaurant
Not a set of good-looking tables dropped into a UI. A full year of trading was simulated first, and every page of the product reads off it. That difference matters: invented numbers only have to look right on one screen, whereas a simulated operation has to reconcile across all of them. The 4.3 kg of brown rice on the waste page comes off the stock ledger, lands in the cost figure, and traces back to how many pots were cooked that morning.
The generation chain
- 1
Outside conditions
Day of week, public holidays, office-tower occupancy, temperature and rain, platform promos
- 2
Footfall
Trend + season + correlated noise (negative binomial, not an average with jitter on top)
- 3
What they ordered
Preferences drift slowly; three menu changes across the year
- 4
True demand that day
This is the answer key. Stored, and kept away from the product
- 5
Spread across the day
Lunch 78%, peak 12:00–12:45; dinner 19%
- 6
What the owner actually prepped
Four-week same-weekday average × 0.95 — by feel, and systematically short
- 7
Sold out, so demand gets cut off
The demand turned away is booked separately; it never appears in sales
- 8
Leftovers, carryover, expiry
Produces the waste log
- 9
How the owner ordered
Produces purchase orders, with late deliveries, substitutions, handwritten dockets
- 10
How stock moved
Produces the daily ledger and monthly counts, with counting error
Each step follows from the one above it. Nothing in here was typed in by hand.
What the restaurant looks like
What got generated
17 tables, 100k rows. Three kinds: master data (what this restaurant has), transactions (what happened each day), and a calendar.
Master data
| Table | Rows | Contents |
|---|---|---|
| bom_dish | 211 | Recipes: which ingredients go in each dish, and how many grams |
| bom_prep | 80 | Prep recipes: how much raw material goes into one batch |
| dim_alias | 37 | Aliases: four different names on four dockets, all the same lettuce |
| dim_calendar | 365 | Calendar: weekday, holidays, temperature, rain, platform promos |
| dim_dish | 23 | Dishes: price, category, launch and delist dates. The menu changed three times |
| dim_ingredient | 94 | Ingredients: shelf life, storage, supplier, batch size, yield |
| dim_supplier | 6 | Suppliers: which days they deliver, lead time, minimum order |
Transactions
| Table | Rows | Contents |
|---|---|---|
| fact_inventory_daily | 25,511 | Stock ledger: opening, received, consumed, expired, closing — daily, per item |
| fact_order_line | 19,007 | Order lines: every dish on every ticket |
| fact_prep_log | 6,549 | Prep log: what was cooked when, how many batches, yield, raw cost |
| fact_price | 20,359 | Purchase prices: daily unit price per ingredient, with seasonality and shocks |
| fact_purchase_header | 696 | Purchase orders: with late deliveries and substitutions; 12% handwritten |
| fact_purchase_line | 4,404 | Purchase lines: what came in, how much, at what price |
| fact_sales_daily | 7,665 | Daily sales: units, revenue and delivery share per dish per day |
| fact_sales_order | 14,669 | Orders: every ticket for 8 weeks, with timestamp, channel, discount, void |
| fact_stock_count | 813 | Stock counts: monthly, and the book never quite matches the shelf |
| fact_waste | 2,207 | Waste log: line by line, split into "prepped, not used" and "raw stock expired" |
The data is messy on purpose
Real dockets are not clean. A demo on tidy data would skip the step that eats the most time in this business. So the generator lays a mess on top of the clean version:
Mixed units
The same item is kg on one docket, jin on another, "two cases" on a third
Inconsistent names
One kind of lettuce goes by four names across dockets
Missing paperwork
About 3% of purchase orders simply cannot be found
Handwritten dockets
About 12% are handwritten, with looser fields
Book vs shelf
Every stock count has a batch of SKUs that do not tie out
We kept the clean version too. So "how much of the mess did it read correctly" is a measurable number, not a claim we make about ourselves.
Three acceptance tests, all passing
The accounting identity
Every item, every day: opening + received − consumed − wasted + count adjustment = closing. Twenty-five thousand ledger rows, and not one is allowed to break.
Statistical health check
How much daily sales swing, how correlated consecutive days are, how often a slow dish sells zero — all have to land inside the range of a real restaurant. This is the most effective guard against data that is too clean.
Blind test
Mix the generated daily sales in with a real restaurant’s and show someone. If they can pick them out at a glance, it is not good enough.
We know the answer key — the one thing simulated data buys you
Because we built this restaurant, we hold a copy of the things a real one can never measure:
What demand really was on a day that sold out — including the part turned away
The tower’s occupancy that day, and what that promo genuinely lifted
Inside the cost gap: how much was over-portioning, prep loss, expiry, staff meals
93.7%
of true demand met
6,872
covers turned away by stockouts
11.9%
of purchases became waste
That answer key is locked outside the product, and the model cannot read it while it is being built — a script checks this every single time, because a model that can see the answers is just proving it can guess what we wrote. The key is used at the end, to mark the work: the model says 8 kg tomorrow, the answer says 8.6, and the difference gets written down.
Fields the product side can never reach
true_demandcensored_qtysellout_timeoffice_occupancypromo_liftlocal_eventgap_overportiongap_prep_lossgap_expiredgap_staff_mealPart 2 — What the product does with it
Six features below. Each one states three things: which of your records it reads, what it computes, and what decision it lets you make in the morning.
How accurate is the forecast
42.0%
error · four-week weekday average
36.4%
error · DESTOCK
−13%
error reduced
Measured across 274 days of walk-forward replay, each day using only data from before it. The comparison is what most restaurants do today. We do not quote an accuracy figure on its own — a number without a baseline means nothing.
How the gap on the home page is computed
recipes say
25.1%
ledger says
27.8%
unaccounted gap
2.7 pts
¥9,059 / mo
Both sides cover the last 30 days and both exclude packaging (that runs a separate 3.4% — the industry definition of food cost has never included it). Left is every dish sold × recipe grams × that day's purchase price. Right is what actually came off the stock ledger. Both come from the restaurant's own records, with none of our assumptions in between.
In one paragraph
Most restaurant software helps you keep books — recording what already happened. DESTOCK is aimed at the decision every morning that has not happened yet: how much to prep today. That decision is made by feel, and getting it wrong costs on both sides — prep too much and it goes in the bin tonight, prep too little and the customer turns around and leaves. You see the first one. You never see the second. Which is why most restaurants are quietly under-prepping.
The restaurant in this dataset bins ¥133,910 a year, and carries another ¥9,059 a month it cannot account for between its two ledgers. Neither figure appears on any report it produces — not because nobody cares, but because nobody can see them.
Disclaimer
"Yummy Bowl" in this demo is a simulated restaurant. All data is program-generated and is not any real restaurant’s trading results. Unit prices for common ingredients such as rice, chicken, lettuce and cooking oil reference a real set of books; the rest are industry benchmarks.