Stock Count Add Documentation¶
Overview¶
The Stock Count Add module (stock.count.add) is a transient wizard model that provides a user-friendly interface for batch-adding count lines to stock counts. It simplifies the process of populating count sessions by offering filtering options (product, category, UOM, lot type) and configuration choices for initializing quantities and prices. This is the base wizard used by both desktop and mobile count interfaces.
Model Information¶
Model Name: stock.count.add
Display Name: Stock Count Add
Transient: ✅ Yes (temporary wizard data)
Features¶
- ✅ Transient model - No permanent database storage
- ✅ Wizard interface - Simplified batch configuration
- ✅ Multiple filters - Product, category, UOM, lot type
- ✅ Quantity/price options - Configure initialization behavior
- ✅ Used by variants - Base for mobile.add and custom.add
Understanding the Add Lines Wizard¶
Purpose¶
The wizard solves the problem of manually creating count lines for many products:
Without Wizard:
# Manual: Create 100 lines individually
for product in products:
get_model("stock.count.line").create({
"count_id": count_id,
"product_id": product.id,
"prev_qty": get_balance(...),
"new_qty": ...,
# ... tedious for each product
})
With Wizard:
# Wizard: Configure once, create many
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id,
"categ_id": category_id,
"qty_type": "previous",
"price_type": "product"
})
get_model("stock.count.add").add_lines([wizard_id])
# Creates all matching lines automatically
Key Fields Reference¶
Required Fields¶
| Field | Type | Description |
|---|---|---|
stock_count_id |
Many2One | Target stock count for line addition |
Filter Fields (Optional)¶
| Field | Type | Description |
|---|---|---|
product_id |
Many2One | Specific product to add (if set, only this product) |
categ_id |
Many2One | Product category filter (all products in category) |
sale_invoice_uom_id |
Many2One | Unit of measure filter (products sold in this UOM) |
lot_type |
Selection | Lot tracking filter (with_lot / without_lot / all) |
Configuration Fields (Optional)¶
| Field | Type | Description |
|---|---|---|
qty_type |
Selection | How to initialize new quantities |
price_type |
Selection | How to initialize cost prices |
Field Options Explained¶
Lot Type Options¶
| Value | Label | Behavior |
|---|---|---|
with_lot |
With Lot | Only add products that require lot/serial tracking |
without_lot |
Without Lot | Only add products without lot/serial tracking |
| (null) | All | Add both lot-tracked and non-tracked products |
Example:
# Count only serialized electronics
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id,
"categ_id": electronics_categ_id,
"lot_type": "with_lot" # Only serialized items
})
Quantity Type Options¶
| Value | Label | Behavior |
|---|---|---|
previous |
Copy Previous Qty | Sets new_qty = prev_qty (verification mode) |
reset |
Set Qty To Zero | Sets new_qty = 0 (blind count mode) |
When to use: - previous: For verification counts where users confirm/adjust expected quantities - reset: For blind counts where users only enter what they physically see
Example:
# Blind count (start at zero)
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id,
"qty_type": "reset" # All quantities start at 0
})
Price Type Options¶
| Value | Label | Behavior |
|---|---|---|
previous |
Copy Previous Cost Price | Uses current stock balance cost |
product |
Copy Cost Price From Product | Uses product master cost_price |
reset |
Set Cost Price To Zero | Sets unit_price = 0 |
Recommendation: Use previous for regular counts, product for fresh inventory valuation.
API Methods¶
add_lines(ids, context)¶
Executes the wizard to add filtered lines to the stock count.
Parameters:
- ids (list): Wizard record ID (transient)
- context (dict): Additional context options
Behavior:
1. Reads wizard configuration
2. Builds filter context
3. Calls stock.count.add_lines() with configured parameters
4. Returns navigation to count form
Returns: Dictionary with navigation to count form
Example:
# Create and execute wizard
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id,
"categ_id": category_id,
"lot_type": "with_lot",
"qty_type": "previous",
"price_type": "product"
})
result = get_model("stock.count.add").add_lines([wizard_id])
# result = {
# "next": {
# "name": "stock_count",
# "mode": "form",
# "active_id": count_id
# }
# }
Common Use Cases¶
Use Case 1: Add Category with Verification¶
# Add all products in category with expected quantities
# Step 1: Create stock count
count_id = get_model("stock.count").create({
"location_id": warehouse_id,
"date": time.strftime("%Y-%m-%d %H:%M:%S"),
"memo": "Electronics Category Count"
})
# Step 2: Create wizard for electronics
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id,
"categ_id": electronics_category_id,
"qty_type": "previous", # Show expected qty
"price_type": "previous" # Use current costs
})
# Step 3: Execute
result = get_model("stock.count.add").add_lines([wizard_id])
# Lines added with expected quantities for verification
Use Case 2: Blind Count Setup¶
# Setup for blind counting (no expected quantities shown)
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id,
"categ_id": warehouse_supplies_id,
"qty_type": "reset", # Start at zero (blind)
"price_type": "product"
})
get_model("stock.count.add").add_lines([wizard_id])
# Result: All lines have new_qty=0
# Users count from scratch without bias
Use Case 3: Single Product Count¶
# Count all lots of a specific product
# High-value item with multiple serial numbers
product_id = 123 # Laptop model
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id,
"product_id": product_id, # Specific product only
"lot_type": "with_lot", # Has serial numbers
"qty_type": "previous",
"price_type": "product"
})
get_model("stock.count.add").add_lines([wizard_id])
# Result: Lines created for all laptop serial numbers
Use Case 4: UOM-Filtered Count¶
# Count items sold by specific unit (e.g., boxes)
# Get "Box" UOM
box_uom_id = get_model("uom").search([["name", "=", "Box"]])[0]
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id,
"sale_invoice_uom_id": box_uom_id, # Only items sold by box
"qty_type": "previous",
"price_type": "previous"
})
get_model("stock.count.add").add_lines([wizard_id])
# Useful for warehouse organized by UOM type
Use Case 5: Multiple Filter Combination¶
# Complex filtering: Category + Lot Type + Price Source
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id,
"categ_id": medical_equipment_id, # Filter 1: Category
"lot_type": "with_lot", # Filter 2: Only serialized
"qty_type": "reset", # Blind count
"price_type": "product" # Fresh product costs
})
get_model("stock.count.add").add_lines([wizard_id])
# Result: Only serialized medical equipment with fresh costs
Best Practices¶
1. Choose Appropriate Qty Type¶
# Good: Match qty_type to counting method
# For VERIFICATION (users check expected):
wizard_config = {
"qty_type": "previous", # Show what system expects
"price_type": "previous"
}
# For BLIND COUNT (no bias):
wizard_config = {
"qty_type": "reset", # Start at zero
"price_type": "product"
}
# Bad: Using previous qty for blind count
# (Defeats purpose of blind counting)
2. Filter to Manageable Size¶
# Good: Use filters to create focused counts
# Count by section/category
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id,
"categ_id": section_a_category_id, # Narrow scope
"qty_type": "previous"
})
# Bad: No filters for large warehouse
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id
# No filters - adds ALL products (could be thousands!)
})
3. Execute Wizard Immediately¶
# Good: Create and execute in same session
wizard_id = get_model("stock.count.add").create({...})
result = get_model("stock.count.add").add_lines([wizard_id])
# Wizard data cleaned up automatically
# Bad: Storing wizard_id for later use
# (Transient data may expire)
4. Validate Count State Before Using¶
# Good: Check count is editable
count = get_model("stock.count").browse(count_id)
if count.state != "draft":
raise Exception("Cannot add lines to completed count")
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id,
# ...
})
# Bad: Not checking count state
# (May fail if count already validated)
5. Provide User Feedback¶
# Good: Show what was added
wizard_id = get_model("stock.count.add").create({...})
result = get_model("stock.count.add").add_lines([wizard_id])
# Check result
count = get_model("stock.count").browse(count_id)
print(f"Added {len(count.lines)} lines to count")
# Guide user to next step
print(f"Ready to count {count.num_lines} items")
Wizard Variants¶
This base wizard has three variants used in different contexts:
| Wizard Model | Context | Usage |
|---|---|---|
stock.count.add |
Desktop | Base desktop interface |
stock.count.mobile.add |
Mobile | Mobile/tablet interface |
custom.stock.count.add |
Custom | Custom counting workflows |
All three share identical: - Field structure - Functionality - Behavior
Only difference: Registration name for different UI contexts
Performance Considerations¶
Wizard Efficiency¶
# Transient wizards are lightweight
# No database storage overhead
# Automatic cleanup after use
# Good: One wizard per action
wizard_id = create_wizard(...)
execute_wizard(wizard_id)
# Done - no cleanup needed
# Bad: Creating persistent records for temporary config
# (Use transient for temporary data)
Large Batch Processing¶
# When adding many lines, wizard delegates to stock.count.add_lines()
# That method handles:
# - Background job support
# - Progress tracking
# - Efficient batch creation
# Configure for large counts:
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id,
"categ_id": large_category_id
})
# Execute (may take time for large datasets)
result = get_model("stock.count.add").add_lines([wizard_id], context={
"job_id": job_id # For progress tracking
})
Troubleshooting¶
"stock_count_id required"¶
Cause: Wizard created without target count.
Solution: Always specify stock_count_id:
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id, # Required!
"categ_id": category_id
})
No Lines Added¶
Cause: Filters too restrictive, no products match criteria.
Solution: Verify products exist matching filters:
# Check products in category
products = get_model("product").search([
["categ_id", "=", category_id],
["type", "=", "stock"]
])
print(f"Products in category: {len(products)}")
# Check stock balances exist
balances = get_model("stock.balance").search([
["location_id", "=", location_id],
["product_id.categ_id", "=", category_id]
])
print(f"Stock balances found: {len(balances)}")
Wizard Execution Error¶
Cause: Count in wrong state or missing required data.
Solution: Validate before executing:
count = get_model("stock.count").browse(count_id)
# Check state
if count.state != "draft":
raise Exception("Count must be in draft state")
# Check location
if not count.location_id:
raise Exception("Count has no location")
# Now safe to use wizard
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id,
# ...
})
Integration with UI¶
Desktop Interface¶
# Typical desktop workflow:
# 1. User opens stock count form
# 2. Clicks "Add Lines" button
# 3. Wizard dialog opens
# 4. User selects filters and options
# 5. Clicks "Add" button
# 6. Wizard executes, returns to count form
# 7. Lines appear in count
Programmatic Usage¶
# For batch operations or automation:
def auto_populate_count(count_id, category_id):
"""Automatically populate count with category"""
wizard_id = get_model("stock.count.add").create({
"stock_count_id": count_id,
"categ_id": category_id,
"qty_type": "previous",
"price_type": "product"
})
result = get_model("stock.count.add").add_lines([wizard_id])
# Verify lines added
count = get_model("stock.count").browse(count_id)
return len(count.lines)
# Usage
lines_added = auto_populate_count(count_id, electronics_id)
print(f"Auto-populated {lines_added} lines")
Related Models¶
| Model | Relationship | Description |
|---|---|---|
stock.count |
Many2One | Target count |
product |
Many2One | Product filter |
product.categ |
Many2One | Category filter |
uom |
Many2One | UOM filter |
stock.count.line |
Created | Lines added by wizard |
stock.balance |
Referenced | Source of quantities |
Version History¶
Last Updated: 2024-10-27
Model Version: stock_count_add.py
Framework: Netforce
Additional Resources¶
- Stock Count Documentation:
stock.count - Stock Count Line Documentation:
stock.count.line - Stock Count Mobile Add:
stock.count.mobile.add(identical variant) - Custom Stock Count Add:
custom.stock.count.add(identical variant) - Transient Models Guide
This documentation is generated for developer onboarding and reference purposes.