Solution guide · Inventory Management

Building Inventory Software With Lot Tracking and Expiry Control: Cost, FEFO and Recall Readiness

The short answer

A focused lot tracking and expiry control build inside an existing inventory or ERP (Enterprise Resource Planning) system typically runs $15,000 to $60,000 over 4 to 9 weeks. As part of a first product release it runs $50,000 to $130,000 in 10 to 16 weeks. A full traceability platform with recall reporting, EDI, multi-site transfers and regulated audit trails runs $150,000 to $350,000 over 6 to 12 months. The number moves on how many places a lot number can be created, whether you owe anyone a recall report in under four hours, and whether the warehouse floor works offline.

What lot tracking actually is once real inventory moves

Most people describe this feature as "add a lot number and an expiry date column to the products table." That model survives about three weeks in production.

The failure looks like this. A distributor receives 40 cases of a product on lot A2291, expiry 2027-03-14. Two weeks later they receive another 60 cases, same lot number A2291, because the supplier reused a lot code across two production runs and gave the second one an expiry of 2027-09-02. Your unique constraint on (product_id, lot_number) rejects the receipt. The warehouse worker, who has a truck at the dock and a driver waiting, types A2291-B. Now you have two lots in your system and one lot in the physical world, and no report will ever reconcile them again.

The correction is that a lot is not an attribute of a product. It is its own entity with its own identity, and the natural key is not the lot number. It is the supplier plus the lot code plus the received date plus the expiry, and even that is not reliably unique. Competent builds generate an internal surrogate lot ID at receipt, treat the supplier's printed lot code as a searchable label rather than a key, and allow two internal lots to share a printed code with a merge tool for when someone confirms they are genuinely the same production run.

The second thing that surprises buyers: expiry is not one date. FEFO picking needs the expiry. Quality holds need a retest date. Customer contracts need a minimum remaining shelf life at time of delivery, often expressed as a percentage. A customer with a 50 percent remaining shelf life clause will reject a 24 month product with 11 months left, and if your system shipped it because it only checked "not expired," you eat the return freight and the restock.

Quantity is not a number, it is a ledger

The naive schema puts a quantity column on the lot row and updates it. This is the single most expensive mistake in this category, because it destroys the thing you actually bought lot tracking for: the ability to answer "where did this lot go" during a recall.

What a real build uses is an append-only movement ledger. Every event is a row: receipt, putaway, pick, transfer, adjustment, cycle count variance, scrap, return. On-hand quantity per lot per location is a projection over that ledger, not a stored truth. You can cache it in a materialized view or a summary table, but the ledger is the source of record and the cache is rebuildable from zero at any time. The moment you can rebuild on-hand from the ledger, most of the terrifying data-corruption class of bugs turn into a reprojection job instead of an incident.

This also solves concurrency. Two pickers hitting the same lot in the same second with a mutable quantity column produces a lost update or a deadlock, depending on your isolation level. With a ledger and a check that the projected balance stays non-negative, you get contention on a narrow row and a clean rejection rather than a phantom negative. In PostgreSQL the pattern that works is SELECT FOR UPDATE on a per (lot, location) balance row inside the transaction that appends the movement, which serializes only the specific bin that two people are actually fighting over.

Budget for backdated corrections. Warehouses discover mistakes days later. Someone will need to reverse a movement from last Tuesday after the month closed. Build reversal entries, never deletes, and decide up front whether a reversal reopens a closed accounting period or posts to the current one. Finance has an opinion about this and it is expensive to change later.

FEFO allocation, and why it is never actually FEFO

First Expired First Out sounds like an ORDER BY expiry_date ASC. In practice the allocation engine has to satisfy competing constraints simultaneously:

Single-lot preference. Many customers, especially in food and pharma, will not accept a pallet split across three lots because their own receiving system creates one record per lot and it triples their work. So the engine prefers the oldest lot that can fill the whole line, not simply the oldest lot.

Quality status. Lots sit in states: quarantine, available, on hold, released, rejected. A lot that failed QC must be invisible to allocation but still visible in on-hand for the finance team, because you own it and it is on your balance sheet.

Minimum remaining shelf life per customer. The same lot is allocatable to customer X and forbidden for customer Y on the same day.

Pick path. Strict FEFO can send a picker to the far end of the warehouse for a lot that expires two days earlier than the one in the next aisle. Real engines apply a tolerance window, typically 7 to 30 days configurable, inside which they optimize for pick distance rather than expiry.

Allocation also has to be reservable and expiring. When an order is picked but not shipped, that lot quantity is committed, not on-hand, not free. If your reservation has no TTL, a cancelled order leaves phantom commitments and your available-to-promise silently drops until someone notices you are refusing orders on stock you have. Reservations need an owner, an expiry and a sweeper job.

Barcodes, GS1 and the scanner reality

Lot and expiry data on a physical label is almost always GS1-128 or a GS1 DataMatrix, and the encoding matters. The relevant Application Identifiers are (01) GTIN, (10) batch or lot number, (17) expiration date, (21) serial number, (37) count. AI (10) is variable length, which means it must be terminated by a Group Separator character, ASCII 29, when it is not the last element in the barcode. Scanners in keyboard-wedge mode frequently swallow or mangle that separator, and the result is a lot number that silently absorbs the next field. You get a lot code of "A229117270314" and nobody notices until the recall.

Configure scanners to emit a visible substitute for the separator, parse with a real GS1 parser rather than a regex, and reject barcodes that do not parse cleanly rather than best-effort them. Note also that AI (17) uses YYMMDD and permits DD = 00, meaning end of that month. A naive date parse turns that into an invalid date or, worse, silently into the first of the month, which shortens your shelf life by up to 30 days on every affected lot.

If you are in US pharma, DSCSA changes the shape of this entirely: you are dealing with serialized units, not just lots, and EPCIS 1.2 or 2.0 event exchange with trading partners. That is a different and much larger project, and any vendor who quotes it at the same price as lot tracking has not done it.

Offline scanning, and what happens when the wifi drops

Warehouse wifi has dead zones, freezers kill batteries and handhelds, and a receiving dock that stops working when the AP reboots is a business outage. So the handheld app buffers locally. Now you have a distributed system, and the interesting question is what happens when two devices both allocate the last 12 units of lot A2291.

Last-write-wins is wrong here and it is what most quick builds do. Because the ledger is append-only, you do not need LWW: you need each device to generate a client-side movement ID (a UUIDv7 or a ULID works, and the time ordering helps), post movements idempotently keyed on that ID, and let the server be the only thing that decides whether the resulting balance is legal. If the server rejects a queued movement because the stock is gone, the device gets a conflict item in a queue that a human resolves, not a silent overwrite. Retries are then free, because posting the same movement ID twice is a no-op.

The parts that get skipped and then cost you: the local queue must survive an app kill and a device reboot, so it is SQLite or IndexedDB, not memory. The device clock is not trustworthy, so store both the device timestamp and the server receipt time and reconcile on server time. And the conflict queue needs a UI, an owner and an escalation, or it silently accumulates until someone finds 400 unresolved items in March.

The recall clock is the real requirement

Nobody buys lot tracking for the lot numbers. They buy it because a regulator, an auditor or a customer will one day ask: which of my customers received lot A2291, and which raw material lots went into it. In the US, FSMA Section 204 and the Food Traceability Rule expect a sortable electronic record produced within 24 hours of request. Many customer quality agreements and internal mock-recall drills demand two to four hours. FDA and comparable bodies routinely run mock recalls as part of audits.

Two directions matter and they are different queries. Trace forward: given an inbound lot, every downstream shipment and customer. Trace backward: given a customer complaint or a finished lot, every input lot and supplier. If you do manufacturing or repacking, the link between them is a genealogy record produced at the work order, and if you did not capture the consumed lots at the moment of production you cannot reconstruct it later at any price.

Test this the way it will actually be used. Pick a random lot from 8 months ago and run both traces on production-scale data. If the query takes 40 seconds against a full ledger, it will time out under the pressure of a real recall. Recursive CTEs over a genealogy edge table with an index on both directions is usually enough up to tens of millions of movements. Past that you precompute a closure table.

Related and routinely missed: audit trail. In FDA-regulated contexts, 21 CFR Part 11 means the record of who changed what, when and why is itself part of the deliverable, with reason-for-change capture on manual adjustments and no ability for an admin to edit history. Bolting that on afterward is a rewrite of your write path.

What this costs, from Digital Heroes delivery experience

Across more than 2,000 projects, the bands we see for this capability:

Focused build inside an existing product: $15,000 to $60,000, 4 to 9 weeks. You already have products, locations and orders. We add the lot entity, the movement ledger, the FEFO allocator, expiry alerting and the two trace reports. The low end assumes one warehouse, no offline, no barcode scanning, one unit of measure.

Part of a first release: $50,000 to $130,000, 10 to 16 weeks. This is lot tracking plus the receiving, picking and shipping flows around it, plus a scanner-driven handheld screen, plus roles and permissions.

Full traceability platform: $150,000 to $350,000, 6 to 12 months. Multi-site, manufacturing genealogy, offline handhelds, EDI or EPCIS exchange with partners, validated audit trail, recall workbench.

What specifically pushes this capability's number up, in rough order of impact:

Manufacturing or repacking. The moment inputs become a different output, you need genealogy, yield reconciliation and partial-consumption handling. Add 40 to 70 percent.

Offline handhelds. The conflict queue, the local store, the idempotency layer and the testing matrix are typically $18,000 to $45,000 on their own.

Regulated audit trail (Part 11, GxP, validated system with IQ/OQ/PQ documentation). Frequently doubles the QA and documentation line, and it is documentation cost more than code cost.

Catch weight and dual units of measure. If you sell by case but stock by kilogram and each case weighs differently, every quantity in the ledger becomes two numbers and every rounding decision becomes a business rule. Add 20 to 35 percent.

Migrating existing inventory. Bringing 5 years of history into a ledger model from a system that stored mutable quantities is its own project, usually 2 to 4 weeks, and the data will be worse than anyone told you.

The thing that does not drive cost much: the number of products. The thing that does: the number of distinct places in your workflow where a lot number can be created or changed. Count them before you brief anyone.

When you should not build this

If lot tracking and expiry control is the main thing you need, and your workflow is broadly standard receiving, putaway, picking and shipping, do not build custom software. Buy it.

For a distributor or a food or supplement business running under about $30M revenue with one to three sites and no unusual process, an off-the-shelf system is the right answer and it is not close. Fishbowl, Cin7, Katana and Odoo all have working lot and expiry tracking. NetSuite and Dynamics 365 Business Central do too at the ERP tier. You will spend $10,000 to $60,000 on implementation and licensing in year one and you will be live in weeks, not months, with the recall report already built and the scanner integration already debugged by someone else.

Build custom when one of these is true. Your process is genuinely non-standard and the off-the-shelf system would require so much customization that you are writing code anyway, just inside someone else's constraints and their upgrade cycle. You are building a product that other companies will use, in which case lot tracking is your product, not your back office. Your lot logic is a competitive advantage, for example allocation that accounts for potency or assay values, blending rules or a cold-chain excursion model that no packaged system expresses. Or you already have a working system that does everything else and the lot layer is the only gap, which is the $15,000 to $60,000 focused build above and is very often the best value on this page.

How to brief and vet a developer for this

Before you talk to anyone, write down: how many physical sites, whether you manufacture or only distribute, whether any customer has a minimum remaining shelf life clause, whether you sell in a different unit than you stock, whether the warehouse has reliable wifi everywhere, and who has ever asked you for a recall report and how fast they wanted it. Those six answers move the price more than everything else combined.

Then ask these questions. They are designed to sort people who have shipped this from people who have read about it.

"Do you store quantity on the lot row or derive it from a movement ledger?" If the answer is a quantity column, they have not run a recall.

"What happens when a supplier reuses a lot code with a different expiry date?" Someone who has shipped this will immediately talk about surrogate keys and merge tooling. Someone who has not will say the unique constraint handles it.

"How do you handle the GS1 AI (10) variable length field and the group separator?" If they have not hit the mangled-separator problem, they have never parsed a real supplier label.

"What does AI (17) with a DD of 00 mean and what do you do with it?" This one is close to a shibboleth. The answer is end of month.

"Two handhelds offline both allocate the last 12 units. Walk me through the resolution." Listen for idempotency keys and a human conflict queue. If you hear "last write wins" or "we sync on reconnect," end the conversation.

"Show me the trace-backward query and tell me its runtime on 10 million movement rows." Anyone who has done a mock recall has this number.

"How do I reverse a movement from a closed accounting period?" Correct answer involves reversal entries and a conversation with your finance team. Wrong answer is an admin edit screen.

And one for the commercial side: ask them what they would put in a fixed-price statement of work and what they would keep hourly. Someone who has shipped this will fix-price the ledger, the schema and the allocator, and will refuse to fix-price the data migration and the supplier label parsing, because both depend on data quality they have not seen yet. That refusal is a good sign.

Research & sources

The evidence behind this guide

Independent findings on why this investment pays off. Every link goes to the primary source.

  1. McKinsey estimates that digitizing the supply chain (Supply Chain 4.0) can cut lost sales by up to 75%, reduce inventories by up to 75%, and lower supply chain operational costs by up to 30%, with up to 30% lower transport and warehousing costs. Source: McKinsey & Company (2016) →
  2. Global retail loses an estimated $1.73 trillion annually to inventory distortion (out-of-stocks and overstocks), equal to about 6.5% of global retail sales, despite $172 billion spent on improvements in the past year. Source: IHL Group (2025) →
  3. The 2015 CHAOS data (based on the modern definition of success) reports that only about 29% of software projects succeed, 52% are challenged, and 19% fail, with the three most important success skills being executive sponsorship, emotional maturity, and user involvement. Source: The Standish Group (reported via InfoQ Q&A with Jennifer Lynch) (2015) →
  4. Companies in the top quartile of McKinsey's Developer Velocity Index had 2014-18 revenue growth four to five times faster than bottom-quartile peers, showing that software-building capability is a driver of business performance, not just a support function. Source: McKinsey & Company (2020) →
Rohan Malhotra · Enterprise Software Consultant

Rohan advises mid-market and enterprise teams on ERP, CRM and custom software, and has led delivery on dozens of business-software builds.

Writes for Digital Heroes, shipping business software for 2,000+ brands across 55+ countries since 2017.

FAQ

Frequently asked questions

How much does it cost to add lot tracking and expiry control to existing inventory software?
A focused build inside a system that already has products, locations and orders typically runs $15,000 to $60,000 over 4 to 9 weeks in Digital Heroes delivery experience. That covers the lot entity, an append-only movement ledger, FEFO allocation, expiry alerting and forward and backward trace reports. It assumes one site, no offline handhelds and a single unit of measure. Add manufacturing genealogy or catch weight and the number moves up 20 to 70 percent.
How long does it take to build lot tracking with expiry control?
4 to 9 weeks for a focused build inside an existing product, 10 to 16 weeks when it ships as part of a first release with receiving, picking and shipping flows around it, and 6 to 12 months for a full multi-site traceability platform. The long pole is almost never the lot schema. It is the data migration from whatever you have now and getting real supplier barcodes to parse.
Can you add lot tracking to our existing app without rebuilding it?
Usually yes, and it is often the best value option. The catch is the write path. If your current system stores a mutable quantity number on the product or lot row, that has to become a movement ledger, which touches every place inventory changes. Counting how many code paths create or modify a quantity is the first thing to do, and it is usually more than the team expects.
What breaks at scale with lot tracking?
Trace-backward queries that ran in 200ms on test data take 40 seconds against tens of millions of movement rows, which is exactly when you need them during a recall. Reservations without a TTL leave phantom commitments so your available-to-promise silently drops and you refuse orders on stock you have. And concurrent pickers on a mutable quantity column produce lost updates or negatives, which is why the ledger plus a per-bin lock is the pattern that holds.
Should we use an off-the-shelf inventory system instead of custom?
If lot and expiry tracking is your main need and your process is standard receiving, putaway, picking and shipping, buy it. Fishbowl, Cin7, Katana, Odoo, NetSuite and Dynamics 365 Business Central all handle lots and expiry, and you will be live in weeks with the recall report already built. Build custom only if your process is genuinely non-standard, your lot logic is a competitive advantage, or you are building a product other companies will use.
We have a $25,000 budget. What can we realistically get?
At $25,000 you can get a solid focused build inside an existing system: lot entity with surrogate keys, movement ledger, FEFO allocation with quality status, expiry alerting, and both trace directions. What you cannot get at that number is offline handhelds, manufacturing genealogy, catch weight, or a Part 11 validated audit trail. If you need any of those, either raise the budget or scope the first phase to the ledger and allocator and add the rest later, which the ledger design makes possible.
Why do vendors quote $8,000 for this when you say $15,000 to $60,000?
Because the $8,000 quote is a lot_number column and an expiry_date column on the products table. That version works until a supplier reuses a lot code, until two pickers hit the same lot at once, or until someone asks for a recall report. Then you pay for it twice: once to build it and once to unwind it. Ask any low quote whether quantity lives on the lot row or in a movement ledger and the answer tells you everything.
What is FEFO and is it just sorting by expiry date?
FEFO means First Expired First Out, and no, it is never a simple sort. A real allocator balances expiry against single-lot preference (many customers reject split-lot pallets), quality hold status, per-customer minimum remaining shelf life clauses, and pick path distance within a configurable tolerance window of typically 7 to 30 days. Strict expiry sorting sends pickers across the warehouse for a two-day difference and gets ignored by the floor.
Do we need offline support for warehouse scanning?
If your warehouse has freezers, metal racking or any wifi dead zone, yes, and it typically adds $18,000 to $45,000. Offline turns this into a distributed system, so the build needs client-generated movement IDs, idempotent posting, a local store that survives a device reboot (SQLite or IndexedDB), and a human-resolved conflict queue. Last-write-wins is the wrong answer here and it is what fast builds do.
How much does custom inventory management software cost for a small business?
A single-location system with receiving, stock movements, and barcode scanning typically runs $15,000 to $40,000, based on Digital Heroes delivery experience across 2,000+ projects. Multi-warehouse, multi-channel builds land between $40,000 and $120,000, and manufacturing or forecasting features push past that. The biggest cost driver is logic rather than screens: lot tracking, unit conversions, and channel sync each add real engineering time.
How many people should be working on my software project?
Three to five for a typical focused build: a project lead, one or two engineers, a designer, and part-time QA, which is the standard shape across 2,000+ Digital Heroes projects. Larger platforms justify 6 to 10, but a ten-person team on a small first version usually signals bill padding rather than horsepower. What predicts success is whether a senior engineer is writing your code daily, not the headcount on the proposal.
Who owns the code when an agency builds my inventory system?
You should, in full, with intellectual property assignment written into the contract before any payment is made. Insist on the code transferring to a repository you control no later than final payment, plus hosting and domain accounts in your own name. If an agency offers to license you their platform instead of assigning the code, you are buying another Cin7 with fewer features.
What does upkeep on a custom inventory system cost per year?
Budget 15 to 20 percent of the build cost per year, so a $50,000 system runs roughly $8,000 to $10,000 annually across Digital Heroes maintenance contracts. That covers hosting, security patches, integration updates when Shopify or Amazon change their APIs, and small improvements. Skipping it is how a channel sync quietly breaks in month nine and corrupts your counts.
What's a realistic timeline for building a custom inventory system?
A usable first version covering receiving, stock movements, scanning, and low-stock alerts ships in 8 to 12 weeks across Digital Heroes inventory builds. Full multi-warehouse systems with Shopify, Amazon, and accounting integrations run 4 to 6 months. Any quote under 6 weeks usually means the vendor has not scoped concurrency handling or data migration.
How do I vet a software agency for an inventory project specifically?
Ask three technical questions before discussing price: how they stop two simultaneous orders claiming the same last unit, whether stock is stored as an append-only movement ledger or a single overwritable quantity field, and how they test channel sync under load before launch. A team that answers fluently has built inventory systems before; one that steers the conversation to screens and design has not. Then ask for a reference from a client whose system has survived at least one peak season.
What happens to my software if the agency shuts down or we stop working together?
Nothing dramatic, if the engagement was set up correctly: the code sits in your repository, hosting runs on your cloud account, and a handover document explains how to deploy and operate the system. Any competent replacement team can then take over in days rather than months. If the agency controls the repo, the servers, or the domain, fix that now, because renegotiating access during a dispute is the most expensive place to discover the problem.
What are the biggest mistakes first-time software buyers make?
Choosing the lowest bid, paying more than 30-40% upfront instead of on milestones, skipping a written specification, and having no maintenance plan for after launch. The most expensive of the four in Digital Heroes rescue projects is the missing spec: without written acceptance criteria, done becomes an argument instead of a checklist, and every disagreement resolves in the vendor's favor. Fix those four and you have avoided most of the ways these projects fail.
Should I hire a freelancer or an agency to build my inventory system?
For a simple single-user stock tracker, a strong freelancer works and costs roughly half as much. Once real revenue flows through the system, choose an agency, because inventory software fails in production rather than in the demo, and a solo developer is a single point of failure during your busiest week. The most expensive engagements Digital Heroes takes on are rescues of freelancer builds after an oversell incident.
Keep reading
let's build

Build something worth launching.

A plan, a team, a timeline, within 24 hours. No decks, no discovery calls. Tell us what you're building and we'll come back with a real scope and a real number.

message us directly · we reply within one business day

mission briefing

Monthly dispatch

Playbooks, real build costs, and what we're shipping. One email a month. No fluff.

visit us

New York HQ

1140 Broadway, Suite 704 · New York, NY 10001

Get directions
Online now

Hey there 👋 How can we help you today?