Skip to content

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:

Open Count → Scan Item → Confirm Qty → Next Item → Complete

Compared to desktop workflow:

Open Count → Select Product → Enter Lot → Enter Qty → Save Line → Next


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

# 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

  1. Large Scan Button: Primary action, center screen
  2. Item Counter: Running total of scanned items
  3. Last Scanned: Shows most recent scan for confirmation
  4. Undo Button: Quick correction of mistakes
  5. Complete Button: Finalize and validate count
  6. Offline Indicator: Shows connection status
  7. 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:

# Auto-save after each scan
# Store count_id in device storage
# On app restart, offer to resume

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

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.