A food distributor's inventory problem is rarely "how much do we have" — it is "how much do we have, in which lot, expiring when, and can I promise a customer 4 weeks of shelf life on delivery." That last clause is the one generic Distribution Edition training glosses over, and it is the one that determines whether the implementation actually works for a food business.
Distribution Edition is the right base
Unlike a bakery or a beverage producer, a pure food distributor usually is not manufacturing anything — cases come in, cases go out, sometimes repacked into smaller units. Acumatica's Distribution Edition (inventory, purchasing, sales order management, warehouse management) is the correct edition, not Manufacturing Edition, unless there is genuine repacking/kitting happening, in which case Kit Assembly (not full BOM/routing) usually covers it.
FEFO picking plus a shelf-life guarantee on the sales order
Base Acumatica supports lot expiration tracking and FEFO-based issue at the item class level, which covers "pick the oldest-expiring stock first." What it does not give you out of the box is a guaranteed-remaining-shelf-life check at order confirmation — many food distribution contracts specify "customer will not accept product with less than 60% of shelf life remaining." That is a validation you add, not a checkbox you tick:
protected virtual void SOLine_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
var line = (SOLine)e.Row;
if (line == null || line.InventoryID == null) return;
var lot = INLotSerStatus.PK.Find(Base, line.InventoryID, line.LotSerialNbr);
if (lot?.ExpireDate == null) return;
var totalShelfDays = GetShelfLifeDays(line.InventoryID);
var remainingDays = (lot.ExpireDate.Value - Accessinfo.BusinessDate.Value).Days;
var remainingPct = (decimal)remainingDays / totalShelfDays;
if (remainingPct < 0.60m)
cache.RaiseExceptionHandling<SOLine.lotSerialNbr>(line,
line.LotSerialNbr,
new PXSetPropertyException("Lot has less than 60% shelf life remaining — customer contract requires 60% minimum.", PXErrorLevel.Warning));
}
I make this a warning rather than a hard block by default — some customers waive the requirement for a discount, and the business needs the option to override with a documented reason, not a wall.
Catch weight items — the other common gap
Products sold and priced by actual weight (a case of chicken sold nominally as "40lb" but actually weighing 38.6lb on receipt) need catch-weight handling: two quantities per transaction, an ordered/nominal quantity and an actual/catch quantity, with pricing keyed to the actual. Acumatica does not have a native catch-weight UOM concept baked into the base sales order screen the way some food-specific ERPs do; the workable pattern is a custom field pair (Catch Weight Actual) on SOLine/POLine plus a costing/pricing extension that prices off the actual field instead of the ordered quantity. This is a real customization, not configuration — say so plainly during scoping if the client sells catch-weight product, because it changes the estimate meaningfully.
Acumatica's standard unit-of-measure conversions (case to each, kg to lb) are fine for fixed, known ratios. Catch weight is fundamentally different — the actual quantity is not knowable until the item is weighed, and it varies per lot. Trying to force it into UOM conversion tables produces silently wrong invoices.
Wrapping up
Food distribution is a Distribution Edition build at its core, with two additions that are commonly assumed to be standard and are not: a guaranteed-shelf-life validation at order time, and catch-weight support if the product line calls for it. Both are buildable on the platform, but scope them explicitly — they are the two things that make a food distributor's implementation feel purpose-built instead of generic.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.