Stock Lot Documentation¶
Overview¶
The Stock Lot module (stock.lot) manages lot numbers, serial numbers, and batch tracking for inventory items. It enables traceability, expiry management, quality control, and detailed tracking of individual units or batches throughout the supply chain.
Model Information¶
Model Name: stock.lot
Display Name: Lot / Serial Number
Key Fields: number
Features¶
- ✅ Audit logging enabled
- ❌ Multi-company support (not enforced)
- ❌ Auto-sync disabled
- ✅ Unique key constraint on number
Lot Tracking Types¶
| Type | Code | Description |
|---|---|---|
| Product | product |
Standard product batches (default) |
| Asset | asset |
Fixed asset tracking |
| Sample | sample |
Laboratory samples |
Lot States¶
| State | Description |
|---|---|
active |
Lot is active and available for use |
inactive |
Lot is deactivated (excluded from balance calculations) |
Key Fields Reference¶
Header Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
number |
Char | ✅ | Unique lot/serial number |
lot_tracking_type |
Selection | ❌ | Product/Asset/Sample (default: product) |
product_id |
Many2One | ❌ | Associated product |
state |
Selection | ❌ | active/inactive |
description |
Text | ❌ | Lot description |
Date Fields¶
| Field | Type | Description |
|---|---|---|
received_date |
DateTime | When lot was received |
mfg_date |
Date | Manufacturing date |
expiry_date |
Date | Expiration date |
life_75_date |
Date | 75% shelf life date (computed) |
life_50_date |
Date | 50% shelf life date (computed) |
life_remain_percent |
Decimal | Remaining shelf life % (computed) |
Physical Properties¶
| Field | Type | Description |
|---|---|---|
weight |
Decimal | Weight (kg) |
net_weight |
Decimal | Net weight (kg) |
gross_weight |
Decimal | Gross weight (kg) |
width |
Decimal | Width dimension |
length |
Decimal | Length dimension |
thickness |
Decimal | Thickness dimension |
diameter |
Decimal | Diameter dimension |
area_m2 |
Decimal | Area in square meters |
temperature |
Decimal | Storage temperature |
Tracking Fields¶
| Field | Type | Description |
|---|---|---|
supp_lot_no |
Char | Supplier's lot number |
po_number |
Many2One | Purchase order reference |
project_id |
Many2One | Associated project |
url |
Char | External URL/link |
ratio |
Char | Conversion ratio |
Quality Control Fields¶
| Field | Type | Description |
|---|---|---|
defect_categ |
Many2One | Defect category |
grade |
Text | Quality grade |
test_description |
Char | Test description |
notes_and_comments |
Char | Additional notes |
Equipment/Asset Fields (for asset tracking)¶
| Field | Type | Description |
|---|---|---|
asset_id |
Many2One | Fixed asset reference |
machine_number |
Many2One | Machine number |
last_calibration_date |
Date | Last calibration |
next_calibration_date |
Date | Next calibration due |
last_maintenance |
Date | Last maintenance |
next_maintenance |
Date | Next maintenance due |
equipment_status |
Selection | active/under_maintenance/out_of_service |
Relationship Fields¶
| Field | Type | Description |
|---|---|---|
comments |
One2Many | Discussion thread |
stock_balances |
One2Many | Current stock quantities by location |
moves |
One2Many | All stock transactions |
expiry_moves |
One2Many | Expiry-related movements |
lot_items |
One2Many | Sub-items in lot |
lot_lines |
One2Many | Lot line details |
validate_lines |
One2Many | Validation records |
Computed Fields¶
| Field | Type | Description |
|---|---|---|
last_location_id |
Many2One | Last known location |
lot_items_summary |
Char | Summary of lot items |
total_weight |
Decimal | Total weight from lot lines |
total_pcs |
Decimal | Total pieces from lot lines |
API Methods¶
1. Create Lot¶
Method: create(vals, context)
Creates a new lot number.
Context Options:
Example:
# Auto-generated lot number
lot_id = get_model("stock.lot").create({
"product_id": 100,
"mfg_date": "2025-10-01",
"expiry_date": "2026-10-01",
"weight": 50.5,
"description": "Batch A from supplier XYZ"
}, context={"sequence_name": "stock_lot"})
# Manual lot number
manual_lot = get_model("stock.lot").create({
"number": "LOT-2025-001",
"product_id": 100,
"lot_tracking_type": "product"
})
2. Update Expired Lots¶
Method: update_expired_lots(context)
Automatically processes expired lots and moves them to expiry location.
Behavior: - Runs as scheduled task - Finds lots past expiry_date - Creates stock movements to expiry location - Uses settings.lot_expiry_journal_id
Example:
3. Get Life Dates¶
Method: get_life_dates(ids, context)
Calculates 75% and 50% shelf life milestone dates.
Returns:
{
lot_id: {
"life_75_date": "2026-03-15", # 25% consumed
"life_50_date": "2026-06-15" # 50% consumed
}
}
Example:
# Automatically computed when mfg_date and expiry_date set
lot = get_model("stock.lot").browse(lot_id)
print(f"75% life at: {lot.life_75_date}")
print(f"50% life at: {lot.life_50_date}")
4. Get Life Remain Percent¶
Method: get_life_remain(ids, context)
Calculates remaining shelf life percentage.
Returns: Decimal percentage of shelf life remaining
Example:
lot = get_model("stock.lot").browse(lot_id)
if lot.life_remain_percent < 25:
print(f"Warning: Only {lot.life_remain_percent}% shelf life remaining")
Computed Fields Functions¶
get_life_dates(ids, context)¶
Calculates 75% and 50% shelf life milestone dates based on mfg_date and expiry_date
get_life_remain(ids, context)¶
Returns percentage of shelf life remaining from current date to expiry
get_last_location(ids, context)¶
Finds most recent location from stock movements
get_lot_items_summary(ids, context)¶
Generates summary text of lot items
update_total_mt(ids, context)¶
Sums total weight from lot lines
update_total_pcs(ids, context)¶
Sums total pieces from lot lines
Common Use Cases¶
Use Case 1: Create Lot with Expiry Tracking¶
# Create lot for perishable product
lot_id = get_model("stock.lot").create({
"product_id": food_product_id,
"mfg_date": "2025-10-01",
"expiry_date": "2025-12-31",
"supp_lot_no": "SUPP-LOT-12345",
"weight": 100.0,
"description": "Fresh batch from Farm ABC"
}, context={"sequence_name": "food_lot"})
# Check computed dates
lot = get_model("stock.lot").browse(lot_id)
print(f"Alert at 75% life: {lot.life_75_date}")
print(f"Alert at 50% life: {lot.life_50_date}")
Use Case 2: Serial Number Tracking¶
# Create individual serial numbers for electronics
for serial in ["SN001", "SN002", "SN003"]:
get_model("stock.lot").create({
"number": serial,
"product_id": laptop_product_id,
"lot_tracking_type": "asset",
"asset_id": asset_id,
"description": f"Laptop serial {serial}"
})
Use Case 3: Equipment Calibration Tracking¶
# Track calibration for measurement equipment
equipment_lot = get_model("stock.lot").create({
"number": "EQUIP-CAL-001",
"product_id": equipment_id,
"lot_tracking_type": "asset",
"last_calibration_date": "2025-10-01",
"next_calibration_date": "2026-10-01",
"equipment_status": "active",
"machine_number_id": machine_id
})
# Check calibration status
lot = get_model("stock.lot").browse(equipment_lot)
if lot.next_calibration_date < date.today():
print("Calibration overdue!")
Use Case 4: FIFO/FEFO Lot Selection¶
# Find oldest lots (FIFO - First In First Out)
oldest_lots = get_model("stock.lot").search(
[["product_id", "=", product_id],
["state", "=", "active"]],
order="received_date"
)
# Find lots expiring soonest (FEFO - First Expired First Out)
expiring_lots = get_model("stock.lot").search(
[["product_id", "=", product_id],
["expiry_date", "!=", None],
["state", "=", "active"]],
order="expiry_date"
)
Best Practices¶
1. Always Set Expiry for Perishable Goods¶
# Good: Set expiry dates
lot_vals = {
"product_id": product_id,
"mfg_date": mfg_date,
"expiry_date": expiry_date # System calculates life dates
}
# System automatically computes:
# - life_75_date
# - life_50_date
# - life_remain_percent
2. Use Lot States for Control¶
# Deactivate expired or problematic lots
get_model("stock.lot").write([lot_id], {
"state": "inactive" # Excluded from balance calculations
})
# Reactivate if resolved
get_model("stock.lot").write([lot_id], {
"state": "active"
})
3. Link to Source Documents¶
# Always link to PO for traceability
lot_vals = {
"number": "LOT-2025-001",
"product_id": product_id,
"po_number_id": purchase_order_id, # Traceability
"supp_lot_no": supplier_lot_number, # Supplier reference
"received_date": datetime.now()
}
Database Constraints¶
Unique Constraint¶
Each lot number must be globally unique across the system.
Related Models¶
| Model | Relationship | Description |
|---|---|---|
stock.balance |
One2Many | Stock quantities per location for this lot |
stock.move |
One2Many | All movements of this lot |
product |
Many2One | Product this lot belongs to |
account.fixed.asset |
Many2One | Fixed asset (for asset tracking) |
stock.lot.item |
One2Many | Sub-items within lot |
stock.lot.line |
One2Many | Lot line details |
pick.validate.line |
One2Many | Validation records |
Troubleshooting¶
"Lot number must be unique"¶
Cause: Duplicate lot number
Solution: Use unique numbers or let system auto-generate
"Expired lots not moving automatically"¶
Cause: Expiry journal not configured
Solution: Configure settings.lot_expiry_journal_id
"Lot excluded from balance"¶
Cause: Lot state is 'inactive'
Solution: Check lot state and reactivate if appropriate
"Missing shelf life dates"¶
Cause: mfg_date or expiry_date not set
Solution: Set both dates for automatic calculation
Configuration Settings¶
Required Settings¶
| Setting | Location | Description |
|---|---|---|
| Lot Sequence | Sequences | For auto-generating lot numbers |
| Expiry Journal | Settings | For automatic expiry movements |
This documentation is generated for developer onboarding and reference purposes.