~/openarch/work/kitchenledger-// case study

A multi-outlet food manufacturing and catering ERP. Live recipe costing, batch planning, and procurement tied directly to stock - replacing outlet-specific costing sheets with one shared runtime.

Saffron Bay Foods·food manufacturing · catering·2026·in production
openarch.in/work/kitchenledger
KitchenLedger interface
//the brief

What we built.

Saffron Bay ran several kitchens off disconnected spreadsheets. We built a system where every recipe costs itself from live ingredient prices, batches plan against par levels, and procurement, inventory and wastage all read from the same numbers across every outlet.

// Know what's on your shelf before it's gone.

domainfood ERP
shapemulti-outlet platform
statusin production
outlets4 · live
stack
reactdjango · drfpostgrescelery · redisqr scanning
//what we shipped
01

Recipe costing

Per-recipe ingredient breakdown with a live cost roll-up, and cost-per-serving derived automatically from yield.

02

Batch planner

A weekly production calendar per outlet - planned versus produced quantity, with variance flagged as it happens.

03

Procurement

Auto-suggested reorder lists from par levels and upcoming batches, with purchase orders and supplier lead-time tracking.

04

Inventory by outlet

Current stock per ingredient, per outlet, with low-stock and near-expiry badges reserved for genuine inventory state.

05

Wastage report

Logged wastage by ingredient, outlet and reason, rolled up to a weekly wastage-cost figure the kitchens actually see.

06

Compliance docs

FSSAI licence tracking per outlet, with expiry reminders surfaced before they lapse rather than after.

//data model
schema.sql5 tables
table ingredients
name, unit, category, avg_unit_cost
table recipes
name, outlet_id, yield_qty, yield_unit
table recipe_lines
recipe_id, ingredient_id, qty, unit
table batches
recipe_id, outlet_id, planned_qty, produced_qty, status
table wastage_logs
outlet_id, ingredient_id, qty, reason, logged_at
// batch status machine
  1. 01planned
  2. 02in_production
  3. 03completed / short_produced
//business logic
recipe-cost.tsLive recipe cost
function recipeCost(lines, unitCost) {
return lines.reduce((total, line) =>
total + line.qty * unitCost[line.ingredient_id], 0)
}
// costPerServing = recipeCost(...) / recipe.yield_qty
wastage-alert.tsWastage alert
function shouldAlert(wastageCost, procurementCost) {
const ratio = wastageCost / procurementCost
return ratio > 0.08 // alert past 8% of procurement spend
}
//on the roadmap

Honest edges.

  • →Validated multi-unit conversion (kg ↔ g, L ↔ ml) across every category
  • →Supplier lead-time folded into auto-reorder suggestions
  • →A fixed wastage-reason taxonomy to replace free-text tagging
//result
~⅓
wastage cost cut in two months
4
kitchens on one live stock
0
outlet-specific spreadsheets left

Saffron Bay cut ingredient wastage cost by roughly a third within the first two months, with all four kitchens working off the same live stock and recipe-cost numbers instead of outlet-specific spreadsheets.