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:
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
Related Models¶
| 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.