DEV Community

Isaac Mwendwa Muthui
Isaac Mwendwa Muthui

Posted on Originally published at factoraerp.com

Packaging Is an Ingredient: Modelling Food-Contact Specs, Supplier Evidence and Packaging Lots in a Food ERP

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

In most inventory systems, packaging is a boring item: a SKU, a unit of measure, a reorder level. In a food factory that is a modelling mistake. The film, bottle or tub that touches the food is closer to an ingredient than to stationery. It needs a specification, evidence that it is safe for contact with this food, an intake check, a lot number, and a link to every batch it was used on.

This post walks through how we think about modelling food-contact packaging 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: packaging "should be safe and suitable for food use" and should not pose a threat to the safety and suitability of food "under the specified conditions of storage and use"; reusable packaging should be "suitably durable, easy to clean and, where necessary, to disinfect" (13.2.9); packaging materials need separate storage from non-food chemicals (9.2.7). Source
  • EAS 39:2001: food-contact packaging "shall be free of contamination, shall not taint the food" (4.3.10), and packaging "shall not be used as a waste receptacle" (10.8.4). Source
  • Kenya Food Hygiene Regulations (Cap. 254): packaging processes and materials must not transmit contaminants to the products (reg. 14(f)), and each container must be marked to identify the producing factory and the lot (reg. 14(d)). Source
  • EU Regulation 1935/2004, Article 17 (a benchmark, not Kenyan law): businesses must be able to identify the businesses from which and to which food contact materials are supplied. Source

Read as a developer, that is four requirements: conditions of use are part of the item's definition, suitability evidence is data with a scope, packaging is lot-tracked, and packaging lots belong in batch genealogy.

1. "Conditions of use" belong on a versioned specification

A packaging item is not suitable in general. It is suitable for a food, at a fill temperature, for a shelf-life. So the specification is a first-class, versioned record, and the item points at the approved version.

create table packaging_spec (
  id            bigserial primary key,
  item_id       bigint not null references item(id),
  version       int not null,
  material      text not null,          -- e.g. 'PET', 'LDPE film 60 micron'
  food_contact  boolean not null,       -- primary packaging?
  intended_use  jsonb not null,         -- {"foods":["juice"],"max_fill_c":85,"shelf_life_days":180}
  status        text not null check (status in ('draft','approved','superseded')),
  approved_by   bigint references app_user(id),
  approved_at   timestamptz,
  unique (item_id, version)
);
Enter fullscreen mode Exit fullscreen mode

A new supplier or material is a new version, not an edit. The old version stays, because batches made last month were packed against it.

2. Suitability evidence is data with a scope and a date

The supplier's food-contact statement is not a PDF in someone's inbox. It is a record linked to the supplier, the item and the spec version it covers, with the conditions it covers and an expiry or review date.

create table packaging_evidence (
  id              bigserial primary key,
  supplier_id     bigint not null references supplier(id),
  spec_id         bigint not null references packaging_spec(id),
  kind            text not null,        -- 'food_contact_statement','test_report'
  covers          jsonb not null,       -- foods / temperatures the document actually covers
  document_url    text not null,
  valid_until     date
);
Enter fullscreen mode Exit fullscreen mode

The useful check is a comparison, not a checkbox: does evidence.covers include everything in spec.intended_use? If the spec says hot fill at 85 °C and the statement only covers ambient use, that is a gap the system can show before the first delivery, not after the first complaint.

3. Receiving creates a lot, and the lot starts on hold

Packaging is received exactly like an ingredient: the supplier's lot number is mandatory, the intake inspection is a record, and the lot cannot be issued until it passes.

type PackagingLot = {
  id: string;
  itemId: string;
  specId: string;          // the spec version in force at receipt
  supplierId: string;
  supplierLot: string;     // required: no supplier lot, no receipt
  qty: number;
  status: 'on_hold' | 'released' | 'rejected';
};

function canIssue(lot: PackagingLot, evidenceOk: boolean): boolean {
  return lot.status === 'released' && evidenceOk;
}
Enter fullscreen mode Exit fullscreen mode

Making supplierLot required is the cheapest traceability control you will ever ship. Making on_hold the default status means a forgotten inspection fails safe.

4. Packaging lots are inputs in batch genealogy

When a roll of film or a pallet of bottles is issued to the line, the issue is a consumption against the production batch, the same table you use for ingredient lots.

-- Which batches did a faulty film lot go on, and where were they shipped?
select b.batch_code, d.customer_id, d.shipped_at
from batch_input bi
join batch b        on b.id = bi.batch_id
left join dispatch d on d.batch_id = b.id
where bi.input_lot_id = :packaging_lot_id;
Enter fullscreen mode Exit fullscreen mode

If packaging is not in batch_input, this query has no answer and the factory holds every batch from the period instead of the affected ones.

5. Reconciliation is a cheap anomaly detector

Issued minus used minus wasted minus returned should be close to zero for each run. Store the four numbers per batch and flag the gap. A large positive gap means packaging went somewhere it should not: into another product, into the bin, or into someone's car. A negative gap means a count or an issue was missed.

Design notes

  • Do not let packaging be a "consumable" type. Consumables are often excluded from lot tracking by default. Food-contact packaging must not be.
  • Model reusable containers separately. Crates and returnable bottles are assets that cycle: they need a cleaning event and an inspection before each reuse, not a receipt.
  • Keep the spec version on the lot. When the spec changes, lots received under the old version still need to be judged against what was approved when they arrived.

If you build software for factories, how do you treat packaging in your data model: as stock, as an ingredient, or as something else?

Top comments (0)