Skip to content

Pick Validate Documentation

Overview

The Pick Validate module (pick.validate) provides a transient wizard interface for validating warehouse picking operations. It allows users to verify picked quantities, scan barcodes, and handle partial pickings by automatically creating back orders for unfulfilled quantities. This module is essential for ensuring picking accuracy and inventory integrity.


Model Information

Model Name: pick.validate
Display Name: Pick Validate
Transient Model: ✅ Yes (data not permanently stored)

Features

  • ✅ Transient model (_transient = True)
  • ❌ Audit logging enabled
  • ❌ Multi-company support
  • ✅ Barcode scanning support
  • ✅ Automatic back order creation

Understanding Transient Models

Transient models are temporary models that: - Store data only during the user's session - Are automatically cleaned up after a period - Perfect for wizards, forms, and validation screens - Don't require permanent database storage


Key Fields Reference

Header Fields

Field Type Required Description
picking_id Many2One The stock picking being validated (stock.picking)

Relationship Fields

Field Type Description
lines One2Many Validation line items (pick.validate.line)

API Methods

1. Initialize Validation Wizard

Method: default_get(field_names, context) / _get_picking(context) / _get_lines(context)

Automatically loads picking data when opening the validation wizard.

Context Parameters:

context = {
    "refer_id": picking_id,              # ID of picking to validate
}

Behavior: - Loads the picking record specified in context - Pre-populates validation lines with picking data - Sets default quantities from original picking

Example:

# Open validation wizard for a picking
context = {"refer_id": 123}
wizard_id = get_model("pick.validate").create({}, context=context)

# Wizard automatically populated with picking data
wizard = get_model("pick.validate").browse(wizard_id)
print(f"Validating picking: {wizard.picking_id.number}")
print(f"Lines loaded: {len(wizard.lines)}")


2. Validate Picking

Method: do_validate(ids, context)

Validates the picking and handles partial fulfillment.

Parameters: - ids (list): Validation wizard ID

Behavior: - Compares validated quantities with original picking quantities - Updates picking lines with validated quantities - Creates back order for unfulfilled quantities - Marks picking as done - Calculates cost amounts - Redirects to appropriate picking form

Returns: Dictionary with navigation and flash message

Example:

# Validate a picking after reviewing quantities
wizard = get_model("pick.validate").browse(wizard_id)

result = wizard.do_validate()
# Returns:
# {
#     "next": {
#         "name": "pick_out",           # or "pick_in", "pick_internal"
#         "mode": "form",
#         "active_id": picking_id
#     },
#     "flash": "Picking PKG-001 validated and back order PKG-002 created"
# }


3. Scan Barcode

Method: scan_barcode(barcode, context)

Scans and identifies products, lots, or containers by barcode.

Parameters: - barcode (string): Scanned barcode value

Returns: Dictionary with identified item information

Behavior: - Searches for product by code or barcode - Falls back to lot number search - Falls back to container number search - Raises exception if not found

Example:

# Scan a product barcode
result = get_model("pick.validate").scan_barcode("123456789")

# Returns product info:
{
    "product": {
        "id": 456,
        "code": "PROD-001",
        "name": "Widget A",
        "barcode": "123456789"
    }
}

# Or lot info:
{
    "lot": {
        "id": 789,
        "number": "LOT-2025-001"
    }
}

# Or container info:
{
    "container": {
        "id": 321,
        "number": "CONT-001"
    }
}


Workflow Integration

Validation Process Flow

Draft Picking → Open Validate Wizard → Scan/Enter Quantities → Validate
                                                    ┌──────────────┴───────────────┐
                                                    ↓                              ↓
                                          Full Quantity?                   Partial Quantity?
                                                    ↓                              ↓
                                          Mark as Done              Create Back Order + Done

Model Relationship Description
stock.picking Many2One Picking being validated
pick.validate.line One2Many Validation line items
stock.move Related Stock movements (via picking)

Common Use Cases

Use Case 1: Full Picking Validation

# 1. Open picking in validation mode
pick_id = 123
context = {"refer_id": pick_id}
wizard_id = get_model("pick.validate").create({}, context=context)

# 2. System loads picking data automatically
wizard = get_model("pick.validate").browse(wizard_id)

# 3. Scan or verify quantities (already pre-filled)
for line in wizard.lines:
    print(f"Product: {line.product_id.name}, Qty: {line.qty}")

# 4. Validate
result = wizard.do_validate()
print(result["flash"])  # "Picking PKG-001 validated"

Use Case 2: Partial Picking with Back Order

# 1. Open validation wizard
wizard_id = get_model("pick.validate").create(
    {},
    context={"refer_id": pick_id}
)

# 2. Adjust quantities for partial picking
wizard = get_model("pick.validate").browse(wizard_id)

for line in wizard.lines:
    # Only 75% of ordered quantity available
    actual_qty = line.qty * 0.75
    line.write({"qty": actual_qty})

# 3. Validate (creates back order automatically)
result = wizard.do_validate()
print(result["flash"])
# "Picking PKG-001 validated and back order PKG-002 created"

Use Case 3: Barcode Scanning During Validation

# 1. Open validation wizard
wizard_id = get_model("pick.validate").create(
    {},
    context={"refer_id": pick_id}
)

# 2. Scan items as they're picked
wizard = get_model("pick.validate").browse(wizard_id)

for line in wizard.lines:
    # Scan barcode to confirm product
    scanned_code = input("Scan barcode: ")
    scan_result = get_model("pick.validate").scan_barcode(scanned_code)

    if "product" in scan_result:
        if scan_result["product"]["id"] == line.product_id.id:
            print("✅ Product confirmed")
            # Enter actual picked quantity
            actual_qty = float(input("Enter picked quantity: "))
            line.write({"qty": actual_qty})
        else:
            print("❌ Wrong product scanned!")

# 3. Validate
result = wizard.do_validate()

Best Practices

1. Use Barcode Scanning

# Good: Use scan_barcode for speed and accuracy
try:
    result = get_model("pick.validate").scan_barcode(scanned_code)
    if "product" in result:
        product_id = result["product"]["id"]
        # Add to validation line
except Exception as e:
    # Handle unknown barcode
    print(f"Barcode not found: {e}")

# Bad: Manual entry prone to errors
product_id = manual_input()  # ❌ Slower, error-prone

2. Always Validate Quantities

# Good: Verify quantities match what was actually picked
wizard = get_model("pick.validate").browse(wizard_id)

for line in wizard.lines:
    # Check physical count
    physical_qty = count_physical_items(line.product_id)

    if physical_qty != line.qty:
        print(f"⚠️ Qty mismatch: Expected {line.qty}, Found {physical_qty}")
        line.write({"qty": physical_qty})  # ✅ Update to actual

# Bad: Blindly validate without checking
wizard.do_validate()  # ❌ May validate incorrect quantities

3. Handle Exceptions Gracefully

# Good: Handle barcode scan failures
try:
    result = get_model("pick.validate").scan_barcode(barcode)
    # Process result
except Exception as e:
    print(f"Scan failed: {e}")
    # Allow manual entry as fallback
    product_id = manual_product_selection()

# Bad: No error handling
result = get_model("pick.validate").scan_barcode(barcode)  # ❌ May crash

Performance Tips

1. Pre-load Picking Data

  • The wizard automatically pre-loads all picking data in _get_lines()
  • No need to manually query picking lines

2. Batch Barcode Operations

# Efficient: Collect all barcodes, then validate
scanned_items = []

for expected_line in wizard.lines:
    barcode = scan_next_barcode()
    scanned_items.append({
        "barcode": barcode,
        "line": expected_line
    })

# Then validate all at once
for item in scanned_items:
    result = get_model("pick.validate").scan_barcode(item["barcode"])
    # Process result

wizard.do_validate()  # ✅ Single validation call

Troubleshooting

"Barcode not found: {barcode}"

Cause: Scanned barcode doesn't match any product code, barcode, lot number, or container number
Solution: - Verify barcode is correct - Check if product/lot/container exists in system - Ensure barcode field is populated on product

"Invalid picking reference"

Cause: refer_id not provided in context or invalid
Solution: Always provide valid picking ID in context when creating wizard

Wizard data not persisting

Cause: Transient model - data automatically cleaned up
Solution: This is expected behavior; complete validation in same session


Testing Examples

Unit Test: Basic Validation

def test_pick_validation():
    # Create a picking
    pick_id = get_model("stock.picking").create({
        "type": "out",
        "lines": [("create", {
            "product_id": prod_id,
            "qty": 10,
            "uom_id": uom_id,
            "location_from_id": from_loc,
            "location_to_id": to_loc
        })]
    })

    # Open validation wizard
    wizard_id = get_model("pick.validate").create(
        {}, 
        context={"refer_id": pick_id}
    )
    wizard = get_model("pick.validate").browse(wizard_id)

    # Verify lines loaded
    assert len(wizard.lines) == 1
    assert wizard.lines[0].qty == 10

    # Validate
    result = wizard.do_validate()

    # Verify picking done
    pick = get_model("stock.picking").browse(pick_id)
    assert pick.state == "done"

Unit Test: Partial Picking

def test_partial_picking():
    # Create picking for 100 units
    pick_id = get_model("stock.picking").create({
        "type": "out",
        "lines": [("create", {
            "product_id": prod_id,
            "qty": 100,
            "uom_id": uom_id,
            "location_from_id": from_loc,
            "location_to_id": to_loc
        })]
    })

    # Validate only 75 units
    wizard_id = get_model("pick.validate").create(
        {},
        context={"refer_id": pick_id}
    )
    wizard = get_model("pick.validate").browse(wizard_id)
    wizard.lines[0].write({"qty": 75})

    result = wizard.do_validate()

    # Verify original picking shows 75
    pick = get_model("stock.picking").browse(pick_id)
    assert pick.lines[0].qty == 75
    assert pick.state == "done"

    # Verify back order created for 25
    back_orders = get_model("stock.picking").search([
        ["ref", "=", pick.number],
        ["id", "!=", pick_id]
    ])
    assert len(back_orders) == 1
    back_order = get_model("stock.picking").browse(back_orders[0])
    assert back_order.lines[0].qty == 25

Unit Test: Barcode Scanning

def test_barcode_scan():
    # Create product with barcode
    prod_id = get_model("product").create({
        "name": "Test Product",
        "code": "PROD-001",
        "barcode": "123456789"
    })

    # Scan barcode
    result = get_model("pick.validate").scan_barcode("123456789")

    # Verify product found
    assert "product" in result
    assert result["product"]["id"] == prod_id
    assert result["product"]["barcode"] == "123456789"

    # Test unknown barcode
    try:
        get_model("pick.validate").scan_barcode("UNKNOWN")
        assert False, "Should raise exception"
    except Exception as e:
        assert "Barcode not found" in str(e)

Security Considerations

Permission Model

  • Access controlled through stock.picking permissions
  • Validation typically requires warehouse staff or manager role

Data Access

  • Transient model - data automatically cleaned up
  • No permanent storage of validation attempts
  • Audit trail maintained through picking state changes

Version History

Last Updated: 2025-10-27
Model Version: pick_validate.py
Framework: Netforce


Additional Resources

  • Pick Validate Line Documentation: pick.validate.line
  • Stock Picking Documentation: stock.picking
  • Stock Move Documentation: stock.move
  • Product Documentation: product

This documentation is generated for developer onboarding and reference purposes.