Your Inventory Is Only as Good as Your Ability to Trace It
A customer calls about a defective batch of electronic components they received last Tuesday. Your warehouse manager opens the ERP, searches the delivery order, and finds… nothing. No lot number, no serial number, no link back to the supplier shipment that brought those components in. The only way to investigate is to manually cross-reference purchase orders, delivery dates, and supplier invoices—a process that takes hours instead of seconds.
This scenario plays out daily in companies that skip lot and serial number configuration during their Odoo implementation. The inventory module ships with powerful traceability features, but they require deliberate setup. Traceability doesn't happen by default—it happens by design.
This guide covers every layer of lot and serial number tracking in Odoo 19: the difference between lots and serials, how to configure products for tracking, how to assign numbers during receipt and delivery, how to run traceability reports, and how to manage expiry dates and product recalls. We include the actual Python models, XML views, and operational workflows so you can implement full traceability in your warehouse.
Lot Tracking vs Serial Number Tracking: When to Use Each
Odoo offers two distinct tracking mechanisms, and choosing the wrong one creates operational headaches that are painful to reverse once you have live inventory. Understanding the difference upfront saves hours of data cleanup later.
| Criteria | Lot Tracking | Serial Number Tracking |
|---|---|---|
| Uniqueness | One lot number covers multiple units (e.g., 500 units in Lot A2024-03) | Each unit gets a globally unique serial number |
| Quantity per number | Any quantity | Exactly 1.00 |
| Best for | Food, chemicals, pharmaceuticals, raw materials, bulk goods | Electronics, machinery, vehicles, high-value assets, warranty items |
| Expiry dates | Set per lot—all units in the lot share the same expiry | Set per serial—rarely used since each unit is individual |
| Recall scope | Recall an entire lot (batch recall) | Recall individual units by serial number |
| Operational overhead | Low—scan one barcode per batch | High—scan every individual unit |
| Odoo field | tracking = 'lot' | tracking = 'serial' |
Ask yourself: "If this product is defective, do I need to recall every unit individually, or can I recall the entire production batch?" If you need individual unit recall (warranty claims, asset tracking), use serial numbers. If batch-level recall is sufficient (food safety, chemical compliance), use lots. If you don't need recall capability at all, don't enable tracking—it adds scanning overhead to every warehouse operation.
Configuring Products for Lot and Serial Number Tracking in Odoo 19
Tracking configuration lives on the product template. Once you set a tracking type and start processing inventory moves, changing the tracking type on a product with existing stock requires careful migration. Get this right during initial setup.
Step 1: Enable Tracking on the Product Form
Navigate to Inventory → Products → Products, open a product, and go to the Inventory tab. Under the Traceability section, set the Tracking field:
# In stock/models/product_template.py
class ProductTemplate(models.Model):
_inherit = "product.template"
tracking = fields.Selection(
selection=[
("none", "No Tracking"),
("lot", "By Lots"),
("serial", "By Unique Serial Number"),
],
string="Tracking",
default="none",
required=True,
help="Ensure the traceability of a storable product "
"in your warehouse.",
)Step 2: Enable Lots & Serial Numbers in Settings
Before the tracking field appears on product forms, you must enable the feature globally. Go to Inventory → Configuration → Settings and activate Lots & Serial Numbers under the Traceability section. This activates the stock.lot model and adds the tracking field to the product form:
<record id="stock.action_stock_config_settings"
model="ir.actions.act_window">
<field name="res_model">res.config.settings</field>
</record>
<!-- The setting controls group_stock_lot -->
<field name="group_stock_lot"
string="Lots & Serial Numbers"
implied_group="stock.group_stock_lot"
help="Track products using lots or serial numbers"/>Step 3: Configure Lot/Serial Auto-Generation (Optional)
Odoo 19 can auto-generate lot or serial numbers on receipts. This avoids manual entry and enforces consistent naming conventions. Configure this under Inventory → Configuration → Settings → Traceability:
class StockPickingType(models.Model):
_inherit = "stock.picking.type"
use_create_lots = fields.Boolean(
string="Create New Lots/Serial Numbers",
default=True,
help="If checked, new lots/serial numbers can be "
"created during this operation type.",
)
use_existing_lots = fields.Boolean(
string="Use Existing Lots/Serial Numbers",
default=True,
help="If checked, existing lot/serial numbers "
"can be selected during this operation.",
)
# Configure auto-generation pattern via sequence:
# Inventory > Configuration > Warehouse Management
# > Operation Types > Receipts
# Set "Lot/Serial Number" sequence prefix, e.g.:
# LOT/{{product.default_code}}/%(year)s-
# This generates: LOT/ELEC-001/2026-00001 Establish a lot/serial naming convention before your first receipt. A good pattern includes the product category, year, and a sequential number: FOOD-2026-00001 for food items, ELEC-SN-2026-00001 for electronics. Once warehouse staff start scanning arbitrary lot names, you'll never get consistency back without a data migration.
Assigning Lot and Serial Numbers During Receipt and Delivery
Configuration means nothing if your warehouse team doesn't assign tracking numbers during operations. Odoo enforces this at the stock move level—you cannot validate a picking for a tracked product without assigning lot or serial numbers. Here's the complete workflow.
Lot Assignment on Receipt (Incoming Shipment)
When a purchase order generates a receipt, tracked products require lot/serial assignment before validation:
- Open the receipt (Inventory → Operations → Receipts)
- Click Detailed Operations (or the serial number icon) on the move line
- For lot-tracked products: enter the lot name (or scan the supplier's lot barcode) and the quantity received in that lot
- For serial-tracked products: enter each serial number individually, or use Generate Serial Numbers to create a range automatically
- Click Validate—Odoo creates the
stock.lotrecords and links them to the stock moves
class StockLot(models.Model):
_name = "stock.lot"
_description = "Lot/Serial"
name = fields.Char(
string="Lot/Serial Number",
required=True, index=True,
)
product_id = fields.Many2one(
"product.product", string="Product",
required=True, index=True,
)
company_id = fields.Many2one(
"res.company", string="Company",
required=True, index=True,
)
# Traceability link
quant_ids = fields.One2many(
"stock.quant", "lot_id",
string="Quants",
)
_sql_constraints = [
("unique_lot",
"unique(name, product_id, company_id)",
"The lot/serial number must be unique "
"per product and company!"),
]Serial Number Assignment on Delivery (Outgoing Shipment)
When a sales order generates a delivery, Odoo requires the warehouse operator to specify which lot or serial number is being shipped. This is the critical link that enables end-to-end traceability—from the supplier who sent you the goods to the customer who received them:
# stock/models/stock_move.py (simplified)
class StockMove(models.Model):
_inherit = "stock.move"
def _action_done(self):
for move in self:
if move.has_tracking != "none":
for ml in move.move_line_ids:
if not ml.lot_id and not ml.lot_name:
raise UserError(_(
"You need to supply a Lot/Serial "
"Number for product: %s",
move.product_id.display_name,
))
if (move.has_tracking == "serial"
and ml.quantity > 1):
raise UserError(_(
"A serial number can only be "
"linked to a single unit of %s. "
"Quantity: %s",
move.product_id.display_name,
ml.quantity,
))
return super()._action_done()For high-volume warehouses, manual lot/serial entry is a non-starter. Use Odoo's Barcode app to scan lot numbers directly during receipt and delivery operations. The barcode app auto-populates the lot field on the move line when a lot barcode is scanned, eliminating typos and reducing processing time per line from ~15 seconds to ~2 seconds.
Traceability Reports: Tracking a Product from Supplier to Customer
The payoff of all this tracking setup is the traceability report—a single view that shows every inventory movement linked to a specific lot or serial number. This is the report your quality team uses during audits, your customer support team uses during complaints, and your compliance team uses during recalls.
Accessing the Traceability Report
Navigate to Inventory → Reporting → Traceability. You can also access it directly from a lot record by clicking the Traceability smart button. The report shows the complete chain:
| Column | What It Shows | Why It Matters |
|---|---|---|
| Reference | The stock picking reference (WH/IN/00042, WH/OUT/00108) | Links to the actual receipt or delivery document |
| Product | Product name and internal reference | Confirms the right product is being traced |
| Lot/Serial | The lot or serial number | The primary search key for traceability |
| Date | Timestamp of the stock movement | Establishes the chronological chain of custody |
| From | Source location (Vendor, WH/Stock, etc.) | Shows where the product came from |
| To | Destination location (WH/Stock, Customer, etc.) | Shows where the product went |
| Quantity | Units moved in this operation | Tracks how many units from this lot/serial were involved |
Upstream & Downstream Tracing
Odoo 19's traceability report supports two critical tracing directions:
- Upstream (backward) tracing: Given a lot/serial shipped to a customer, trace back to the supplier who provided it. Essential for: "Customer reported defect—which supplier batch did this come from?"
- Downstream (forward) tracing: Given a supplier lot, trace forward to every customer who received units from it. Essential for: "Supplier issued recall on batch X—which customers are affected?"
# Find all customers who received a specific lot
lot = self.env["stock.lot"].search([
("name", "=", "LOT-2026-00042"),
("product_id.default_code", "=", "ELEC-001"),
], limit=1)
# Get all outgoing moves for this lot
outgoing_moves = self.env["stock.move.line"].search([
("lot_id", "=", lot.id),
("picking_id.picking_type_code", "=", "outgoing"),
("state", "=", "done"),
])
# Extract affected customers
affected_customers = outgoing_moves.mapped(
"picking_id.partner_id"
)
for partner in affected_customers:
print(f"Customer: {partner.name}, "
f"Delivery: {partner.picking_ids.mapped('name')}")The traceability report is only as reliable as the data entered during operations. If warehouse staff bypass lot assignment by using force validation or if they assign placeholder lot numbers like "TBD" or "123", your traceability chain is broken. Disable the "No Validation" setting on picking types for tracked products and train your team to treat lot/serial assignment as a mandatory step, not an optional one.
Expiry Date Tracking and Product Recall Management
For industries dealing with perishable goods—food, pharmaceuticals, cosmetics, chemicals—lot tracking without expiry dates is incomplete. Odoo 19's Expiration Dates feature extends the stock.lot model with date fields that integrate into inventory valuation, FEFO picking strategies, and automated alerts.
Enabling Expiry Date Tracking
Activate Expiration Dates under Inventory → Configuration → Settings → Traceability. This adds four date fields to every lot:
class StockLot(models.Model):
_inherit = "stock.lot"
use_date = fields.Datetime(
string="Best Before Date",
help="Date after which the product may begin to "
"deteriorate, without necessarily being dangerous.",
)
expiration_date = fields.Datetime(
string="Expiration Date",
help="Date after which the product becomes dangerous "
"and must not be consumed or sold.",
)
removal_date = fields.Datetime(
string="Removal Date",
help="Date after which the product should be removed "
"from stock (before expiration).",
)
alert_date = fields.Datetime(
string="Alert Date",
help="Date on which an alert should be raised about "
"the lot approaching expiry.",
)FEFO Removal Strategy (First Expiry, First Out)
With expiry dates on lots, you can configure locations or product categories to use FEFO (First Expiry, First Out) as the removal strategy. This ensures the warehouse picks the lot closest to expiry first, reducing waste:
<record id="stock_location_fefo_example"
model="stock.location">
<field name="name">Cold Storage</field>
<field name="usage">internal</field>
<field name="removal_strategy_id"
ref="product_expiry.removal_fefo"/>
</record>
<!-- Available removal strategies:
- FIFO (First In, First Out) - default
- LIFO (Last In, First Out)
- FEFO (First Expiry, First Out)
- Closest location -->Managing Product Recalls
When a recall is necessary, the workflow combines traceability reports with expiry tracking:
- Identify the affected lot(s) using the supplier's recall notice or your quality inspection records
- Run a downstream traceability report to find every customer who received units from the affected lot
- Check current stock—search
stock.quantto find units from the recalled lot still in your warehouse - Create a scrap order or internal transfer to quarantine recalled stock
- Notify affected customers using the partner list from step 2
# Find all quants for the recalled lot
recalled_lot = self.env["stock.lot"].search([
("name", "=", "FOOD-2026-00089"),
], limit=1)
quants = self.env["stock.quant"].search([
("lot_id", "=", recalled_lot.id),
("location_id.usage", "=", "internal"),
("quantity", ">", 0),
])
# Create scrap orders for each quant
for quant in quants:
self.env["stock.scrap"].create({
"product_id": quant.product_id.id,
"lot_id": quant.lot_id.id,
"scrap_qty": quant.quantity,
"location_id": quant.location_id.id,
"origin": "RECALL: FOOD-2026-00089",
}) Configure a scheduled action (ir.cron) to check the alert_date field daily and send email notifications to the quality team. Odoo's product_expiry module includes a basic cron for this, but most companies need to customize the notification logic—adding Slack alerts, filtering by product category, or escalating to management when high-value lots approach expiry.
3 Lot & Serial Number Tracking Mistakes That Break Traceability in Production
Changing the Tracking Type on a Product with Existing Stock
You decide to switch a product from lot tracking to serial tracking (or from "no tracking" to "lot") after you already have inventory in the system. Odoo lets you change the tracking field on the product form, but existing quants don't retroactively get lot/serial numbers. Your on-hand stock shows up as untracked, the traceability report has gaps, and warehouse operations for that product start throwing validation errors because old quants have no lot assigned.
Never change tracking type on products with existing stock in a live environment. Instead: (1) create an inventory adjustment to zero out the untracked stock, (2) change the tracking type, (3) create a new inventory adjustment to add the stock back with proper lot/serial numbers. This preserves the audit trail and ensures every quant has a valid tracking reference going forward.
Allowing Duplicate Lot Names Across Different Products
A supplier ships two different products—resistors and capacitors—both labeled with the same lot number "2026-B42." Your warehouse team creates lot "2026-B42" for the resistors and tries to create the same lot name for the capacitors. In Odoo, the stock.lot uniqueness constraint is per product per company, so this works. But when your quality team searches for lot "2026-B42" in the traceability report, they get mixed results from two different products, creating confusion during recall investigations.
Prefix lot numbers with the product's internal reference: RES-001-2026-B42 and CAP-005-2026-B42. Configure the lot sequence on the receiving operation type to include %(product.default_code)s as a prefix. This makes every lot name globally unique in practice, even though the database constraint only requires per-product uniqueness.
Not Tracking Lots Through Manufacturing (Only Receipts and Deliveries)
You set up lot tracking for incoming raw materials and outgoing finished goods, but your manufacturing orders don't consume specific lots. The bill of materials uses generic components without lot assignment. When a customer reports a defect in finished product lot "FG-2026-00150," you can trace it to the delivery and back to the manufacturing order—but you cannot determine which raw material lots were used in that production run. Your traceability chain has a gap right in the middle.
Enable "Track Components" on your manufacturing operation type and configure work order steps to scan raw material lot numbers during consumption. In the manufacturing order, use Detailed Operations on each component move to assign the specific lot being consumed. This creates a complete traceability link: supplier lot → raw material lot consumed in MO → finished good lot → customer delivery. The cost is additional scanning time per work order, but the traceability value is essential for industries with regulatory requirements (FDA, ISO 22000, IATF 16949).
What Full Traceability Saves Your Business
Lot and serial number tracking isn't a warehouse project—it's a risk management investment:
With downstream traceability, identifying affected customers takes minutes instead of days. A targeted recall costs a fraction of a blanket recall that pulls all stock.
FEFO removal strategies and automated expiry alerts ensure the oldest stock ships first. Companies report 30-40% reduction in write-offs within the first year of implementation.
FDA, ISO, and GMP auditors require end-to-end traceability. Without it, you fail the audit. With Odoo's traceability reports, you pass it with a single search.
For a food distributor handling 2,000 SKUs with a 3% annual spoilage rate on $5M inventory, reducing spoilage by 40% through FEFO and expiry alerts saves $60,000 per year. Add the avoided cost of a single blanket recall (typically $250K-$500K for a mid-size distributor), and the ROI of proper traceability setup is measured in multiples, not percentages.
Optimization Metadata
Complete guide to Odoo 19 lot and serial number tracking. Configure products, assign lots on receipt, run traceability reports, manage expiry dates, and handle recalls.
1. "Lot Tracking vs Serial Number Tracking: When to Use Each"
2. "Configuring Products for Lot and Serial Number Tracking in Odoo 19"
3. "Traceability Reports: Tracking a Product from Supplier to Customer"
4. "Expiry Date Tracking and Product Recall Management"
5. "3 Lot & Serial Number Tracking Mistakes That Break Traceability in Production"