Skip to content

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

[Pending] → Scan products → [Validate] → Create Picking → [Done]
                                   Clear form
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:

{
    "flash": "Goods receipt GR-2025-001 created successfully",
    "focus_field": "related_id"
}

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:

# Clear and start over
get_model("barcode.receive").clear([wizard_id])


UI Events (onchange methods)

onchange_product

Triggered when product is scanned/selected in a line.

Updates: - Sets default UoM from product

Usage:

# Automatically called when product selected
# Sets line["uom_id"] = product.uom_id.id


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?
}

# 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

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_received on PO lines

Sales Orders (Returns)

  • Links to sale.order for customer returns
  • Uses delivered quantities

Stock Picking

  • Creates stock.picking records
  • 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.