Mobile Count Documentation¶
Overview¶
The Mobile Count module (mobile.count) provides a mobile-optimized interface for stock counting using smartphones, tablets, and other handheld devices. This module shares the same core functionality as stock.count but is specifically designed for mobile workflows, featuring barcode scanning, touch-friendly interfaces, and offline capability support. It enables warehouse staff to perform physical counts directly from mobile devices without requiring desktop access.
Model Information¶
Model Name: mobile.count
Display Name: Mobile Count
Key Fields: number, company_id
Features¶
- ✅ Audit logging enabled (
_audit_log) - ✅ Multi-company support (
company_id) - ✅ Unique number per company
- ✅ Draft/Done/Voided workflow
- ✅ Mobile-optimized UI
- ✅ Barcode scanner integration
- ✅ Touch-friendly interface
- ✅ Offline capability (implementation dependent)
Mobile-Specific Features¶
1. Barcode Scanning Priority¶
The mobile interface emphasizes barcode scanning as the primary input method:
- Quick Scan Mode: Scan items rapidly without manual data entry
- Auto-Increment: Scanning same item multiple times increments quantity
- Instant Feedback: Visual/audio confirmation on successful scan
- Error Handling: Clear mobile-friendly error messages
2. Touch-Optimized Interface¶
- Large Touch Targets: Buttons and input fields sized for finger input
- Swipe Gestures: Navigate between count lines efficiently
- Minimal Typing: Reduce keyboard usage in favor of scanning/tapping
- Portrait/Landscape: Adaptive layout for device orientation
3. Device Camera Integration¶
- Built-in Camera: Use device camera for barcode scanning
- QR Code Support: Scan QR codes in addition to barcodes
- Photo Capture: Optionally photograph counted items for verification
4. Simplified Workflow¶
Mobile count streamlines the counting process:
Compared to desktop workflow:
Understanding Mobile Count vs Desktop Count¶
Shared Functionality¶
Both mobile.count and stock.count use the same:
- Data model and database tables
- Validation logic
- Stock movement generation
- Count line management
- State workflow (draft/done/voided)
Mobile-Specific Differences¶
| Aspect | Desktop Count | Mobile Count |
|---|---|---|
| Primary Input | Keyboard/Mouse | Barcode Scanner |
| Interface | Full desktop UI | Touch-optimized mobile UI |
| Navigation | Multiple windows/tabs | Single-screen workflow |
| Data Entry | Manual field entry | Scan-based entry |
| Workflow | Flexible, detailed | Streamlined, simplified |
| Typical Use | Office setup | Warehouse floor |
Key Fields Reference¶
Header Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
number |
Char | ✅ | Unique count reference number (auto-generated) |
memo |
Char | ❌ | Brief note (mobile keyboards friendly) |
location_id |
Many2One | ✅ | Warehouse/location being counted |
date |
DateTime | ✅ | Date and time of the count |
description |
Char | ❌ | Detailed description (optional on mobile) |
state |
Selection | ✅ | Current status (draft/done/voided) |
company_id |
Many2One | ❌ | Company (defaults to active company) |
journal_id |
Many2One | ❌ | Stock journal for movements |
Computed Fields¶
| Field | Type | Description |
|---|---|---|
total_cost_amount |
Decimal | Total value of new inventory amounts |
total_prev_qty |
Decimal | Sum of all previous quantities |
total_new_qty |
Decimal | Sum of all new counted quantities |
num_lines |
Integer | Number of count lines |
Relationship Fields¶
| Field | Type | Description |
|---|---|---|
lines |
One2Many | Count lines (stock.count.line) |
moves |
One2Many | Generated stock movements |
comments |
One2Many | Comments and discussions |
API Methods¶
All API methods from stock.count are available. See the main Stock Count Documentation for full method details.
Mobile-Optimized Methods¶
on_barcode(context)¶
The primary method for mobile counting workflow.
Handles barcode scans during counting to quickly add or increment product quantities.
Context:
context = {
"data": {
"id": count_id # Mobile count ID
},
"barcode": "LOT123456" # Scanned barcode
}
Mobile Workflow:
1. User scans barcode with device camera
2. System looks up lot by barcode number
3. Finds product associated with lot
4. If product/lot exists in count: Increments new_qty by 1
5. If not exists: Creates new line with new_qty = 1
6. Provides instant visual feedback
Example:
# Mobile barcode scan handler
get_model("mobile.count").on_barcode(context={
"data": {"id": count_id},
"barcode": "LOT-2024-001"
})
# Auto-creates or increments count line
Mobile Counting Workflows¶
Workflow 1: Rapid Scanning (Recommended)¶
# Optimized for mobile devices
# 1. Create mobile count
count_id = get_model("mobile.count").create({
"location_id": warehouse_id,
"date": time.strftime("%Y-%m-%d %H:%M:%S"),
"memo": "Mobile Count - Aisle A"
})
# 2. User scans items with mobile device
# Each scan handled by on_barcode method:
# Scan 1: LOT-001 → Creates line with qty=1
# Scan 2: LOT-001 → Increments to qty=2
# Scan 3: LOT-002 → Creates new line with qty=1
# ...continues...
# 3. Validate when complete
get_model("mobile.count").validate([count_id])
Workflow 2: Pre-Populated Mobile Count¶
# For guided counting on mobile
# 1. Create count with expected items
count_id = get_model("mobile.count").create({
"location_id": warehouse_id,
"date": time.strftime("%Y-%m-%d %H:%M:%S"),
"memo": "Mobile Guided Count"
})
# 2. Pre-populate lines from balance
get_model("mobile.count").add_lines([count_id], context={
"qty_type": None, # Start at zero
"price_type": "product"
})
# 3. Mobile user scans each item to update quantities
# 4. Missing scans indicate zero count
# 5. Validate when complete
Workflow 3: Blind Mobile Count¶
# User counts without seeing expected quantities
# 1. Create blank count
count_id = get_model("mobile.count").create({
"location_id": warehouse_id,
"date": time.strftime("%Y-%m-%d %H:%M:%S"),
"memo": "Blind Count - Section B"
})
# 2. User scans only what they physically see
# System creates lines only for scanned items
# 3. After scanning complete, system compares to expected
count = get_model("mobile.count").browse(count_id)
count.update_prev_qtys() # Fetch expected quantities
# 4. Review variances
for line in count.lines:
if line.new_qty != line.prev_qty:
print(f"Variance: {line.product_id.code}")
# 5. Validate
get_model("mobile.count").validate([count_id])
Common Use Cases¶
Use Case 1: Mobile Spot Check¶
# Quick spot check using mobile device
# Setup on mobile device
count_id = get_model("mobile.count").create({
"location_id": warehouse_id,
"date": time.strftime("%Y-%m-%d %H:%M:%S"),
"memo": "Spot Check - Fast Movers"
})
# User walks to shelf and scans items
scanned_barcodes = []
def handle_mobile_scan(barcode):
"""Called when mobile device scans barcode"""
try:
get_model("mobile.count").on_barcode(context={
"data": {"id": count_id},
"barcode": barcode
})
scanned_barcodes.append(barcode)
# Mobile UI shows success feedback
return {"status": "success", "message": f"Counted: {barcode}"}
except Exception as e:
# Mobile UI shows error
return {"status": "error", "message": str(e)}
# Mobile app calls this for each scan
# Example scans:
handle_mobile_scan("LOT-001") # → Creates line, qty=1
handle_mobile_scan("LOT-001") # → Increments to qty=2
handle_mobile_scan("LOT-002") # → Creates line, qty=1
# Complete on mobile or desktop
get_model("mobile.count").validate([count_id])
Use Case 2: Cycle Count with Mobile¶
# Mobile cycle counting workflow
# 1. Generate cycle count list
cycle_products = get_model("cycle.stock.count").search_browse([
["location_id", "=", warehouse_id],
["xyz_categ", "=", "x_"] # High-priority items
])
# 2. Create mobile count
count_id = get_model("mobile.count").create({
"location_id": warehouse_id,
"date": time.strftime("%Y-%m-%d %H:%M:%S"),
"memo": "Mobile Cycle Count - X Items"
})
# 3. Add expected items
for cc in cycle_products:
prod = cc.product_id
prev_qty = get_model("stock.balance").get_qty_phys(
warehouse_id, prod.id
)
get_model("stock.count.line").create({
"count_id": count_id,
"product_id": prod.id,
"prev_qty": prev_qty,
"new_qty": 0, # User will scan to update
"uom_id": prod.uom_id.id
})
# 4. User scans items on mobile device
# Mobile UI shows checklist of expected items
# Scans update quantities
# 5. Validate
get_model("mobile.count").validate([count_id])
Use Case 3: Multi-User Mobile Count¶
# Multiple mobile users counting different sections
# Coordinator creates section counts
sections = [
{"name": "Aisle A", "user": "user1"},
{"name": "Aisle B", "user": "user2"},
{"name": "Aisle C", "user": "user3"}
]
count_ids = {}
for section in sections:
count_id = get_model("mobile.count").create({
"location_id": warehouse_id,
"date": time.strftime("%Y-%m-%d %H:%M:%S"),
"memo": f"Mobile Count - {section['name']} - {section['user']}"
})
count_ids[section['name']] = count_id
# Assign to user's mobile device
print(f"{section['user']}: Count {count_id} for {section['name']}")
# Each user scans their section on mobile device
# Counts are independent and can be validated separately
# Validate all when complete
for count_id in count_ids.values():
get_model("mobile.count").validate([count_id])
Use Case 4: Mobile Count with Photo Verification¶
# Mobile count with photo evidence (requires custom implementation)
count_id = get_model("mobile.count").create({
"location_id": warehouse_id,
"date": time.strftime("%Y-%m-%d %H:%M:%S"),
"memo": "Mobile Count with Photos"
})
def mobile_scan_with_photo(barcode, photo_data):
"""
Handle mobile scan with photo capture
(Custom implementation required)
"""
# Process barcode scan
get_model("mobile.count").on_barcode(context={
"data": {"id": count_id},
"barcode": barcode
})
# Store photo as attachment
# (Requires custom file storage implementation)
# save_photo_attachment(count_id, barcode, photo_data)
return {"status": "success", "barcode": barcode}
# Mobile user scans and takes photo of each item
# Useful for high-value items or audits
Use Case 5: Offline Mobile Counting¶
# Mobile counting with offline support
# (Requires offline-capable mobile app)
class OfflineMobileCount:
"""
Offline mobile counting handler
"""
def __init__(self):
self.offline_scans = []
self.count_id = None
def create_offline_count(self, location_id):
"""Create count that will sync later"""
self.count_id = f"OFFLINE-{time.time()}"
self.offline_scans = []
return self.count_id
def scan_offline(self, barcode):
"""Record scan while offline"""
self.offline_scans.append({
"barcode": barcode,
"timestamp": time.time()
})
print(f"Offline scan recorded: {barcode}")
def sync_to_server(self):
"""Sync when connection restored"""
if not self.offline_scans:
return
# Create real count
count_id = get_model("mobile.count").create({
"location_id": warehouse_id,
"date": time.strftime("%Y-%m-%d %H:%M:%S"),
"memo": "Synced from offline mobile count"
})
# Process all offline scans
for scan in self.offline_scans:
get_model("mobile.count").on_barcode(context={
"data": {"id": count_id},
"barcode": scan["barcode"]
})
print(f"Synced {len(self.offline_scans)} scans to count {count_id}")
self.offline_scans = []
return count_id
# Usage:
offline_counter = OfflineMobileCount()
offline_counter.create_offline_count(warehouse_id)
# While offline:
offline_counter.scan_offline("LOT-001")
offline_counter.scan_offline("LOT-002")
offline_counter.scan_offline("LOT-001")
# When online:
synced_count_id = offline_counter.sync_to_server()
Best Practices¶
1. Mobile-First Design¶
# Good: Optimize for mobile workflow
# Mobile-friendly count creation
count_id = get_model("mobile.count").create({
"location_id": warehouse_id,
"date": time.strftime("%Y-%m-%d %H:%M:%S"),
"memo": "Mobile Count" # Short memo for mobile typing
})
# Start with zero quantities (blind count)
get_model("mobile.count").add_lines([count_id], context={
"qty_type": None, # Don't show expected qty on mobile
"price_type": "product"
})
# User scans items
# Mobile UI only shows: Scan button, Current item, Quantity
2. Handle Barcode Scan Errors Gracefully¶
# Good: User-friendly mobile error handling
def mobile_scan_handler(count_id, barcode):
"""Mobile-optimized scan handler"""
try:
get_model("mobile.count").on_barcode(context={
"data": {"id": count_id},
"barcode": barcode
})
return {
"success": True,
"message": "✓ Item counted",
"sound": "beep_success.mp3"
}
except Exception as e:
error_msg = str(e)
# Mobile-friendly error messages
if "Lot not found" in error_msg:
return {
"success": False,
"message": "⚠️ Barcode not recognized. Try again or enter manually.",
"sound": "beep_error.mp3",
"action": "show_manual_entry"
}
elif "Stock count not yet saved" in error_msg:
return {
"success": False,
"message": "⚠️ Please save count first",
"action": "prompt_save"
}
else:
return {
"success": False,
"message": f"❌ Error: {error_msg}",
"sound": "beep_error.mp3"
}
3. Provide Real-Time Feedback¶
# Good: Keep mobile users informed
def get_mobile_count_status(count_id):
"""Real-time status for mobile UI"""
count = get_model("mobile.count").browse(count_id)
return {
"count_number": count.number,
"items_scanned": len(count.lines),
"total_quantity": sum(l.new_qty for l in count.lines),
"last_scan": count.lines[-1].product_id.code if count.lines else None,
"can_validate": count.state == "draft" and len(count.lines) > 0
}
# Mobile UI displays:
# "Items Scanned: 47"
# "Total Qty: 234"
# "Last: PROD-123"
4. Minimize Mobile Data Entry¶
# Good: Reduce typing on mobile devices
# Pre-fill everything possible
count_id = get_model("mobile.count").create({
"location_id": warehouse_id, # Auto-detected from user
"date": time.strftime("%Y-%m-%d %H:%M:%S"), # Auto-set
"memo": f"Mobile-{user_name}-{time.strftime('%H%M')}" # Auto-generated
})
# Use dropdowns instead of typing
# Use number pads instead of full keyboards
# Use barcodes instead of manual SKU entry
5. Battery and Performance Optimization¶
# Good: Optimize for mobile device constraints
# Batch operations to reduce network calls
def batch_mobile_scans(count_id, barcodes):
"""Process multiple scans efficiently"""
results = []
for barcode in barcodes:
try:
get_model("mobile.count").on_barcode(context={
"data": {"id": count_id},
"barcode": barcode
})
results.append({"barcode": barcode, "status": "success"})
except Exception as e:
results.append({"barcode": barcode, "status": "error", "message": str(e)})
return results
# Mobile app queues scans and sends in batches
# Reduces battery drain from network activity
Mobile UI Considerations¶
Recommended Mobile Interface Elements¶
- Large Scan Button: Primary action, center screen
- Item Counter: Running total of scanned items
- Last Scanned: Shows most recent scan for confirmation
- Undo Button: Quick correction of mistakes
- Complete Button: Finalize and validate count
- Offline Indicator: Shows connection status
- Progress Bar: Visual progress indicator
Screen Layout Example (Pseudo-code)¶
┌─────────────────────────┐
│ Mobile Count #MC-001 │
│ Aisle A - 47 items │
├─────────────────────────┤
│ │
│ [ SCAN BARCODE ] │ ← Large button
│ │
├─────────────────────────┤
│ Last Scanned: │
│ PROD-123 (Qty: 5) │
│ │
│ [Undo] [Manual Entry] │
├─────────────────────────┤
│ Items: 47 Qty: 234 │
│ ████████░░ 80% │
│ │
│ [ COMPLETE COUNT ] │
└─────────────────────────┘
Troubleshooting¶
Barcode Not Scanning¶
Cause: Camera permission not granted or poor lighting.
Solution:
- Check mobile device camera permissions
- Ensure adequate lighting
- Clean camera lens
- Try manual barcode entry as fallback
Duplicate Scans¶
Cause: User accidentally scans same item multiple times.
Solution: Implement undo functionality:
def undo_last_scan(count_id):
"""Remove or decrement last scan"""
count = get_model("mobile.count").browse(count_id)
if count.lines:
last_line = count.lines[-1]
if last_line.new_qty > 1:
last_line.write({"new_qty": last_line.new_qty - 1})
else:
last_line.delete()
Mobile App Crashes During Count¶
Cause: Loss of connection or app error.
Solution: Implement auto-save and recovery:
Slow Scan Response¶
Cause: Network latency or server load.
Solution:
- Implement optimistic UI updates
- Cache product/lot data locally
- Batch sync operations
- Use offline mode when necessary
Performance Tips¶
1. Pre-load Product Data¶
# Load frequently scanned products to mobile device
popular_products = get_model("product").search_browse([
["type", "=", "stock"],
# Add criteria for frequently counted items
])
# Cache on mobile device for offline access
2. Minimize API Calls¶
# Bad: One API call per scan
for barcode in scanned_items:
get_model("mobile.count").on_barcode(...) # Network call
# Good: Batch processing
# Queue scans locally, sync in batches
3. Use Lightweight Responses¶
# Return minimal data to mobile
def mobile_scan_response(count_id, barcode):
# Don't return full count object
return {
"success": True,
"item_count": get_line_count(count_id), # Just the count
"total_qty": get_total_qty(count_id)
}
Related Models¶
| Model | Relationship | Description |
|---|---|---|
stock.count |
Parallel | Desktop equivalent |
stock.count.line |
One2Many | Count lines (shared) |
stock.move |
One2Many | Stock movements (shared) |
stock.location |
Many2One | Warehouse location |
stock.lot |
Referenced | For barcode lookup |
product |
Referenced | Products being counted |
Version History¶
Last Updated: 2024-10-27
Model Version: mobile_count.py
Framework: Netforce
Additional Resources¶
- Stock Count Documentation:
stock.count - Stock Count Line Documentation:
stock.count.line - Barcode Scanning Best Practices
- Mobile App Development Guide
This documentation is generated for developer onboarding and reference purposes.