DEV Community

Isaac Mwendwa Muthui
Isaac Mwendwa Muthui

Posted on Originally published at factoraerp.com

Waste Is an Output, Not a Subtraction: Modelling By-Products, Waste Streams and Licensed Collections in a Food ERP

By Isaac Muthui, CEO, Tamalaki Business Network Ltd. Originally published at factoraerp.com.

Most production software models a batch as inputs in, one product out. Whatever doesn't make it into the finished-goods line is "loss", a single number that nobody can explain. In a food factory that number is made of very different things: rejects, line purges, bran, whey, pomace, film offcuts, chemical containers and effluent. Some of it is sold as feed, some goes to a licensed collector, some is hazardous. If the system can't tell them apart, nobody can manage them.

This post walks through how we think about modelling waste and by-product streams in an ERP for food and agro manufacturers in East Africa. The operational checklist behind it is in the original article.

What the domain rules say

The requirements read like a spec:

  • Codex CXC 1-1969 (9.2.1): waste "should be collected, disposed of by trained personnel and, where appropriate, disposal records maintained"; containers "for waste, by-products and inedible or hazardous substances should be specifically identifiable". Source
  • EAS 39:2001 (10.8): skips emptied "at least once per week", factory waste containers "emptied daily" and not mistakable for food containers. Source
  • Kenya's Sustainable Waste Management Act, 2022: segregate "non-hazardous waste into organic and non-organic fractions" (s.12); a private sector entity's annual monitoring report specifies "the actual quantities of waste generated by the entity" (s.19); waste goes to "a person who is licensed to transport and dispose of the waste". Source

Translated into engineering terms: every production output has a type, every waste movement has a stream, a quantity and a destination, and every collection points at a counterparty whose licence has an expiry date.

Outputs are typed, not leftover

The first design choice is to stop calculating waste as input - output. Declare it.

RecipeOutput     (id, recipe_id, item_id, output_type, expected_pct)
                 output_type IN ('primary','co_product','byproduct','waste')
ProductionOutput (id, run_id, recipe_output_id, qty_kg, recorded_at, recorded_by)
Enter fullscreen mode Exit fullscreen mode

A recipe that turns 1,000 kg of maize into flour, bran and screenings says so up front. When the run closes, each output is weighed and recorded against the run. "Unexplained loss" becomes the residual after declared outputs, which is the number you actually want to drive towards zero.

By-products are items. Bran or whey that leaves the gate as feed gets an item code, a lot, a stock location and a dispatch record, exactly like finished goods. If a raw-material lot is later found to have a problem, the by-product lot is in the same trace.

Waste never enters saleable stock. A waste output posts to a waste location (or no stock at all), so it can't be picked, sold or counted as inventory value.

Streams, movements and collections

WasteStream      (id, name, category, hazardous, container_spec, removal_frequency, owner_id)
                 category IN ('organic','plastic','general_dry','hazardous','effluent')
WasteMovement    (id, stream_id, source_run_id NULL, source_area, qty_kg, moved_at, moved_by)
WasteCollection  (id, stream_id, collector_id, qty_kg, collected_at, destination, document_ref)
Collector        (id, name, licence_no, licence_expires_on)
Enter fullscreen mode Exit fullscreen mode

A few rules make this useful:

Category is fixed on the stream. Segregation at source maps one-to-one to streams. If organic and plastic are mixed in one stream, the report can't separate them later.

Block expired collectors. A collection against a collector whose licence_expires_on is in the past should fail validation, or at least raise an exception for the EHS lead. That turns a filing-cabinet check into a system rule.

Schedules generate tasks. removal_frequency on the stream creates cleaning-schedule tasks (empty line bins every shift, skip weekly). A missed task is a hygiene finding, not a surprise at audit.

The queries that matter

Monthly totals by category, which is close to what an annual monitoring report asks for:

SELECT ws.category, date_trunc('month', wc.collected_at) AS month, SUM(wc.qty_kg) AS kg
FROM waste_collection wc
JOIN waste_stream ws ON ws.id = wc.stream_id
GROUP BY ws.category, month
ORDER BY month, ws.category;
Enter fullscreen mode Exit fullscreen mode

And the one production managers care about, waste as a share of output per product and shift:

SELECT pr.product_id, pr.shift, 
       SUM(po.qty_kg) FILTER (WHERE ro.output_type = 'waste') /
       NULLIF(SUM(po.qty_kg), 0) AS waste_share
FROM production_output po
JOIN recipe_output ro ON ro.id = po.recipe_output_id
JOIN production_run pr ON pr.id = po.run_id
WHERE pr.started_at >= now() - interval '30 days'
GROUP BY pr.product_id, pr.shift
ORDER BY waste_share DESC;
Enter fullscreen mode Exit fullscreen mode

When waste share on one shift starts climbing, it's usually a yield, equipment or training problem showing up early, long before it shows up in the month-end cost.

Takeaways

  • Declare outputs on the recipe: primary, co-product, by-product, waste.
  • Treat by-products as stock items with lots, not as negative waste.
  • Model waste streams with a fixed category so segregation is reportable.
  • Put licence expiry on collectors and validate against it.
  • Measure waste where it is made, per run, not by subtraction at month end.

The full 10-step operational checklist, with references to Codex, EAS 39, Kenya's food hygiene regulations and the Sustainable Waste Management Act, is on the FactoraERP blog.

Top comments (0)