Barcode Receive Documentation¶
Overview¶
The Barcode Receive module (barcode.receive) provides a barcode-scanning interface for quickly receiving goods into inventory. It's a transient (wizard-style) model that simplifies the goods receipt process through barcode scanning, allowing warehouse staff to receive products from purchase orders or returns without manually creating stock pickings.
Model Information¶
Model Name: barcode.receive
Display Name: Barcode Receive
Key Fields: None (transient model)
Features¶
- ❌ Audit logging disabled (transient model)
- ❌ Multi-company support (not enforced)
- ❌ Full-text content search (transient model)
- ✅ Transient model (wizard - data not permanently stored)
Purpose¶
This module enables: - ✅ Fast barcode-based goods receiving - ✅ Integration with purchase orders - ✅ Automatic stock picking creation - ✅ Lot/serial number assignment via barcode - ✅ Container tracking during receiving - ✅ Real-time inventory updates
State Flow¶
| State | Description |
|---|---|
pending |
Goods receipt created but not yet completed |
done |
Goods receipt validated and stock updated immediately |
Key Fields Reference¶
Header Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
location_from_id |
Many2One | ❌ | Source location (non-internal) |
location_to_id |
Many2One | ❌ | Destination location (internal only) |
journal_id |
Many2One | ❌ | Stock journal for receiving |
related_id |
Reference | ❌ | Link to PO or SO |
state |
Selection | ✅ | pending/done |
Line Fields (barcode.receive.line)¶
| Field | Type | Required | Description |
|---|---|---|---|
wizard_id |
Many2One | ✅ | Parent wizard |
product_id |
Many2One | ✅ | Product being received |
qty |
Decimal | ✅ | Quantity received |
uom_id |
Many2One | ✅ | Unit of measurement |
qty2 |
Decimal | ❌ | Secondary quantity |
lot_id |
Many2One | ❌ | Lot/serial number |
container_to_id |
Many2One | ❌ | Target container |
Relationship Fields¶
| Field | Type | Description |
|---|---|---|
lines |
One2Many | Scanned product lines |
related_id |
Reference | Purchase order or sales order |
API Methods¶
1. Create Barcode Receive Session¶
Method: create(vals, context)
Creates a new barcode receiving session.
Parameters:
vals = {
"location_from_id": supplier_loc_id, # Optional: supplier location
"location_to_id": warehouse_loc_id, # Optional: warehouse location
"journal_id": journal_id, # Optional: receiving journal
"related_id": "purchase.order,123", # Optional: link to PO
"state": "done" # Optional: pending/done (default: done)
}
Returns: int - Wizard ID
Example:
# Create receiving session for purchase order
wizard_id = get_model("barcode.receive").create({
"related_id": "purchase.order,456",
"location_to_id": 10, # Main warehouse
"journal_id": 1, # Goods receipt journal
"state": "done"
})
2. Fill Products from Order¶
Method: fill_products(ids, context)
Automatically populates the product list from the related purchase or sales order.
Behavior: - For Purchase Orders: Adds remaining qty to receive (qty - qty_received) - For Sales Orders: Adds delivered qty (for returns) - Skips products with zero remaining quantity - Throws exception if no products to receive
Example:
# After linking to PO, fill products automatically
wizard = get_model("barcode.receive").browse(wizard_id)
wizard.fill_products()
# This populates wizard.lines with products from the PO
3. Validate and Create Picking¶
Method: validate(ids, context)
Creates stock picking from scanned items and completes the receive.
Behavior:
- Validates that product lines exist
- Creates stock.picking with type="in"
- Links to related PO/SO if specified
- Automatically sets picking to done if state="done"
- Clears wizard after successful validation
- Returns success message with picking number
Returns:
Example:
# Validate and create goods receipt
result = get_model("barcode.receive").validate([wizard_id])
print(result["flash"]) # "Goods receipt GR-2025-001 created successfully"
4. Clear Session¶
Method: clear(ids, context)
Clears all data from the wizard to start fresh.
Behavior: - Removes location_from_id - Removes related_id - Deletes all lines - Keeps wizard open for new session
Example:
UI Events (onchange methods)¶
onchange_product¶
Triggered when product is scanned/selected in a line.
Updates: - Sets default UoM from product
Usage:
onchange_related¶
Triggered when related PO/SO is selected.
Updates: - Sets location_from_id based on order type: - Purchase Order → Supplier location - Sales Order → Customer location (for returns)
Example:
# When PO selected, supplier location auto-populated
wizard.related_id = "purchase.order,123"
# location_from_id automatically set to supplier location
Common Use Cases¶
Use Case 1: Receive Purchase Order with Barcode¶
# 1. Create barcode receive session
wizard_id = get_model("barcode.receive").create({
"location_to_id": warehouse_loc_id,
"journal_id": receiving_journal_id,
"state": "done" # Complete immediately
})
wizard = get_model("barcode.receive").browse(wizard_id)
# 2. Link to purchase order
wizard.write({
"related_id": "purchase.order,456"
})
# 3. Auto-fill products from PO
wizard.fill_products()
# 4. Scan barcodes and update quantities/lots
for line in wizard.lines:
# Warehouse staff scans:
# - Product barcode (already filled from PO)
# - Lot barcode
# - Container barcode (if using containers)
line.write({
"lot_id": scanned_lot_id,
"container_to_id": scanned_container_id
})
# 5. Validate and complete
result = wizard.validate()
print(result["flash"])
# Output: "Goods receipt GR-2025-001 created successfully"
Use Case 2: Quick Receive Without PO¶
# 1. Create session
wizard_id = get_model("barcode.receive").create({
"location_from_id": supplier_loc_id,
"location_to_id": warehouse_loc_id,
"journal_id": 1,
"state": "done"
})
# 2. Scan products manually (no PO link)
get_model("barcode.receive.line").create({
"wizard_id": wizard_id,
"product_id": product_id_1,
"qty": 10,
"uom_id": uom_id,
"lot_id": lot_id_1
})
get_model("barcode.receive.line").create({
"wizard_id": wizard_id,
"product_id": product_id_2,
"qty": 25,
"uom_id": uom_id,
"lot_id": lot_id_2
})
# 3. Validate
get_model("barcode.receive").validate([wizard_id])
Use Case 3: Receive with Containers¶
# Receive products directly into specific containers/pallets
wizard_id = get_model("barcode.receive").create({
"location_to_id": warehouse_loc_id,
"journal_id": 1
})
# Scan product into container
get_model("barcode.receive.line").create({
"wizard_id": wizard_id,
"product_id": product_id,
"qty": 100,
"uom_id": uom_id,
"lot_id": scanned_lot_id,
"container_to_id": pallet_id # Assign to pallet
})
# Validate - creates picking with container assignment
get_model("barcode.receive").validate([wizard_id])
Use Case 4: Staged Receiving (Pending State)¶
# Create receiving session but don't complete immediately
wizard_id = get_model("barcode.receive").create({
"location_to_id": staging_area_id,
"journal_id": 1,
"state": "pending" # Don't complete yet
})
# Scan products during day
# ... add lines ...
# Validate - creates picking in pending state
get_model("barcode.receive").validate([wizard_id])
# Later, warehouse manager approves the picking
get_model("stock.picking").approve([picking_id])
get_model("stock.picking").set_done([picking_id])
Best Practices¶
1. Always Set Locations¶
# Good: Explicit locations
wizard_vals = {
"location_from_id": supplier_loc_id,
"location_to_id": warehouse_loc_id,
"journal_id": journal_id
}
# Avoid: Missing locations
wizard_vals = {
"journal_id": journal_id # Where are products going?
}
2. Use Related Orders When Possible¶
# Good: Link to PO for traceability
wizard.write({
"related_id": "purchase.order,123"
})
wizard.fill_products() # Auto-populate from PO
# Less ideal: Manual entry
# ... manually add each line ...
3. Validate Scanned Data¶
# Good: Validate before creating lines
def scan_product(barcode):
# Lookup product by barcode
products = get_model("product").search([
["barcode", "=", barcode]
])
if not products:
raise Exception(f"Product not found for barcode: {barcode}")
return products[0]
# Then create line
product_id = scan_product(scanned_barcode)
get_model("barcode.receive.line").create({
"wizard_id": wizard_id,
"product_id": product_id,
"qty": 1,
"uom_id": uom_id
})
4. Clear Session Between Orders¶
# Good: Clear between different orders
wizard.validate() # Complete first order
wizard.clear() # Clear for next order
# Avoid: Mixing products from different orders
# ... scan order 1 products ...
# ... scan order 2 products ... # Mixed!
# wizard.validate() # Unclear which order
Related Models¶
| Model | Relationship | Description |
|---|---|---|
barcode.receive.line |
One2Many | Scanned product lines |
stock.picking |
Created | Generates goods receipt |
stock.location |
Many2One | From/to locations |
stock.journal |
Many2One | Receiving journal |
purchase.order |
Reference | Source purchase order |
sale.order |
Reference | Source sales order (returns) |
product |
Many2One | Products being received |
stock.lot |
Many2One | Lot/serial numbers |
stock.container |
Many2One | Target containers |
Workflow Integration¶
Process Flow¶
1. Create Session
↓
2. Link to PO (optional)
↓
3. Fill Products (if PO linked)
↓
4. Scan Barcodes
- Product barcode
- Lot barcode
- Container barcode
↓
5. Validate
↓
6. System Creates:
- stock.picking (type="in")
- stock.move records
- Updates stock balances
↓
7. Clear Session
↓
8. Ready for next receiving
Performance Tips¶
1. Batch Receiving¶
# Good: Receive all items before validating
wizard = get_model("barcode.receive").browse(wizard_id)
# Scan all 100 items
for i in range(100):
scan_and_add_line(wizard_id)
# Single validation
wizard.validate() # Creates one picking with 100 lines
# Avoid: Validate after each item
for i in range(100):
scan_and_add_line(wizard_id)
wizard.validate() # Creates 100 separate pickings!
wizard.clear()
2. Use fill_products() for POs¶
# Good: Auto-populate from PO
wizard.write({"related_id": "purchase.order,123"})
wizard.fill_products() # Fast, automatic
# Avoid: Manual line creation when PO exists
for po_line in po.lines:
get_model("barcode.receive.line").create({...}) # Slow, error-prone
Troubleshooting¶
"Product list is empty"¶
Cause: Trying to validate without scanning any products
Solution: Add at least one product line before validating
"No products remaining to receive"¶
Cause: All PO items already fully received
Solution: Check PO status, or receive without linking to PO
"Supplier location not found"¶
Cause: No location with type="supplier" exists
Solution: Create supplier location in stock.location
"Customer location not found"¶
Cause: No location with type="customer" exists (for returns)
Solution: Create customer location in stock.location
Security Considerations¶
Data Access¶
- Transient model - data not permanently stored
- Session-specific - each user has own wizard instance
- No audit trail (use stock.picking audit for tracking)
Integration Points¶
Purchase Orders¶
- Links to
purchase.order - Auto-fills remaining quantities
- Updates
qty_receivedon PO lines
Sales Orders (Returns)¶
- Links to
sale.orderfor customer returns - Uses delivered quantities
Stock Picking¶
- Creates
stock.pickingrecords - Type automatically set to "in"
- Can create in pending or done state
Configuration¶
Required Setup¶
| Item | Location | Description |
|---|---|---|
| Supplier Location | stock.location | Type="supplier" |
| Internal Location | stock.location | Type="internal" for warehouse |
| Receiving Journal | stock.journal | Type="in" for goods receipts |
Version History¶
Last Updated: October 2025
Model Version: barcode_receive.py
Framework: Netforce
Additional Resources¶
- Stock Picking Documentation:
stock.picking - Stock Move Documentation:
stock.move - Stock Location Documentation:
stock.location - Barcode Station Documentation:
barcode.station
This documentation is generated for developer onboarding and reference purposes.