Stock Count Mobile Line Documentation¶
Overview¶
The Stock Count Mobile Line module (stock.count.mobile.line) represents individual count line records used in mobile counting interfaces. This is the same model as stock.count.line but registered under a mobile-specific context. Each line captures product count details including previous system quantities, new physical counts, cost information, and lot/serial numbers—all optimized for mobile device display and interaction.
Model Information¶
Model Name: stock.count.line (shared with desktop)
Display Name: Stock Count Line (Mobile Context)
Key Fields: None (detail records)
Features¶
- ❌ No audit logging (parent count is audited)
- ❌ No multi-company (inherits from parent)
- ✅ Cascade delete (removed when count deleted)
- ✅ Mobile-optimized display fields
- ✅ Computed cost amounts
- ✅ Lot/serial tracking support
- ✅ Touch-friendly data entry
Mobile-Specific Considerations¶
Display Optimization for Mobile¶
Mobile interfaces require: - Minimal fields shown: Only essential information visible - Large touch targets: Easy tap/select on small screens - Progressive disclosure: Details shown only when needed - Offline support: Lines cached for offline editing
Key Differences: Mobile vs Desktop Line View¶
| Aspect | Desktop View | Mobile View |
|---|---|---|
| Fields Shown | All fields visible | Essential fields only |
| Layout | Multi-column table | Single-column list |
| Edit Mode | Inline editing | Full-screen form |
| Sorting | Complex multi-field | Simple single field |
| Actions | Right-click menus | Swipe gestures |
Key Fields Reference¶
Essential Mobile Fields (Priority Display)¶
| Field | Type | Mobile Priority | Description |
|---|---|---|---|
product_id |
Many2One | ⭐⭐⭐ | Product name/code - PRIMARY |
new_qty |
Decimal | ⭐⭐⭐ | Physical count - EDITABLE |
prev_qty |
Decimal | ⭐⭐ | System quantity - READ ONLY |
lot_id |
Many2One | ⭐⭐ | Serial/lot number (if applicable) |
uom_id |
Many2One | ⭐ | Unit of measure |
Secondary Mobile Fields (Detail View)¶
| Field | Type | Description |
|---|---|---|
bin_location |
Char | Warehouse location/bin |
prev_cost_price |
Decimal | Previous cost per unit (computed) |
unit_price |
Decimal | New cost per unit |
prev_cost_amount |
Decimal | Previous total cost |
new_cost_amount |
Decimal | New total cost (computed) |
Supporting Fields¶
| Field | Type | Description |
|---|---|---|
count_id |
Many2One | Parent count (cascade delete) |
lot_weight |
Decimal | Weight from lot (if tracked) |
cyclecount_id |
Many2One | Associated cycle count program |
Mobile Display Patterns¶
Pattern 1: List View (Scan Mode)¶
┌─────────────────────────┐
│ PROD-123 ⭐│ ← Current item (just scanned)
│ Laptop Computer │
│ Qty: 5 (Expected: 8) │ ← Variance highlight
├─────────────────────────┤
│ PROD-124 │
│ Desktop Monitor │
│ Qty: 12 (Expected: 12) │ ← Match (green)
├─────────────────────────┤
│ PROD-125 │
│ Keyboard - USB │
│ Qty: 0 (Expected: 25) │ ← Large variance (red)
└─────────────────────────┘
[Scan Next Item]
Pattern 2: Detail View (Edit Mode)¶
┌─────────────────────────┐
│ ← Back PROD-123 ⋮ │
├─────────────────────────┤
│ Laptop Computer │
│ Model: ThinkPad X1 │
│ │
│ Bin Location: A-03-15 │
│ Lot: LOT-2024-001 │
│ │
│ System Qty: 8 units │
│ │
│ Physical Count: │
│ ┌─────────────────────┐ │
│ │ 5 │ │ ← Large number pad
│ └─────────────────────┘ │
│ │
│ Variance: -3 units │ ← Calculated automatically
│ │
│ [ SAVE ] [ CANCEL ] │
└─────────────────────────┘
Pattern 3: Quick Entry (Barcode Focus)¶
┌─────────────────────────┐
│ Items: 47 Total: 234│
├─────────────────────────┤
│ │
│ Last Scanned: │
│ PROD-123 → Qty: 5 │ ← Instant feedback
│ │
│ [ SCAN NEXT ] │ ← Primary action
│ │
│ Quick Actions: │
│ [+1] [-1] [Edit] │ ← Touch buttons
│ │
└─────────────────────────┘
Computed Fields Functions¶
get_prev_cost_price(ids, context)¶
Calculates previous average cost per unit for mobile display.
Mobile Usage:
# Display in detail view
line = get_model("stock.count.line").browse(line_id)
mobile_display = {
"product": line.product_id.name,
"prev_qty": line.prev_qty,
"prev_cost": f"${line.prev_cost_price:.2f}/unit" # Computed
}
get_new_cost_amount(ids, context)¶
Calculates new total cost for mobile summary.
Mobile Usage:
# Show in count summary
total_value = sum(line.new_cost_amount for line in count.lines)
mobile_summary = {
"items": len(count.lines),
"total_value": f"${total_value:,.2f}"
}
Mobile API Patterns¶
1. Get Line for Mobile Display¶
def get_mobile_line_data(line_id):
"""Format line data for mobile display"""
line = get_model("stock.count.line").browse(line_id)
# Calculate variance
variance = (line.new_qty or 0) - (line.prev_qty or 0)
variance_pct = (variance / line.prev_qty * 100) if line.prev_qty else 0
return {
"id": line.id,
"product_code": line.product_id.code,
"product_name": line.product_id.name,
"bin_location": line.bin_location or "N/A",
"lot_number": line.lot_id.number if line.lot_id else None,
"system_qty": float(line.prev_qty or 0),
"counted_qty": float(line.new_qty or 0),
"variance": float(variance),
"variance_pct": f"{variance_pct:+.1f}%",
"status": "match" if variance == 0 else "variance",
"uom": line.uom_id.name
}
# Mobile API endpoint
mobile_data = get_mobile_line_data(line_id)
2. Update Line from Mobile¶
def update_mobile_line(line_id, new_qty):
"""Update count line from mobile input"""
line = get_model("stock.count.line").browse(line_id)
# Validate count state
if line.count_id.state != "draft":
return {
"success": False,
"error": "Count is locked"
}
# Update quantity
line.write({"new_qty": new_qty})
# Return updated data for mobile
return {
"success": True,
"line": get_mobile_line_data(line_id),
"count_total": sum(l.new_qty for l in line.count_id.lines)
}
# Mobile app calls this
result = update_mobile_line(line_id, 5.0)
3. Batch Load for Mobile¶
def get_mobile_count_lines(count_id, limit=50, offset=0):
"""
Load count lines for mobile (paginated)
"""
lines = get_model("stock.count.line").search_browse([
["count_id", "=", count_id]
], limit=limit, offset=offset, order="id")
return {
"lines": [get_mobile_line_data(l.id) for l in lines],
"total": len(get_model("stock.count.line").search([
["count_id", "=", count_id]
])),
"limit": limit,
"offset": offset
}
# Mobile scrolling loads more
page1 = get_mobile_count_lines(count_id, limit=50, offset=0)
page2 = get_mobile_count_lines(count_id, limit=50, offset=50)
Mobile-Optimized Use Cases¶
Use Case 1: Mobile Line Edit¶
# User taps line in mobile app to edit
def mobile_edit_line_handler(line_id):
"""Handle mobile line tap"""
# Load line data
line = get_model("stock.count.line").browse(line_id)
# Format for mobile editor
mobile_form = {
"title": line.product_id.code,
"fields": [
{
"label": "Product",
"value": line.product_id.name,
"readonly": True
},
{
"label": "Bin Location",
"value": line.bin_location,
"readonly": True
},
{
"label": "Expected Qty",
"value": float(line.prev_qty),
"readonly": True,
"hint": f"{line.uom_id.name}"
},
{
"label": "Physical Count",
"value": float(line.new_qty),
"type": "number",
"editable": True,
"focus": True, # Auto-focus for quick entry
"keyboard": "numeric" # Mobile numeric keyboard
}
],
"actions": [
{"label": "Save", "action": "save"},
{"label": "Cancel", "action": "cancel"}
]
}
return mobile_form
# Mobile app displays form
form_data = mobile_edit_line_handler(line_id)
Use Case 2: Mobile Variance Highlighting¶
# Highlight variances in mobile list
def get_mobile_lines_with_status(count_id):
"""Get lines with visual status indicators"""
lines = get_model("stock.count.line").search_browse([
["count_id", "=", count_id]
])
mobile_lines = []
for line in lines:
variance = (line.new_qty or 0) - (line.prev_qty or 0)
variance_pct = (variance / line.prev_qty * 100) if line.prev_qty else 0
# Determine mobile display status
if variance == 0:
status = "match"
color = "green"
icon = "✓"
elif abs(variance_pct) < 5:
status = "minor"
color = "yellow"
icon = "⚠"
else:
status = "major"
color = "red"
icon = "❗"
mobile_lines.append({
"id": line.id,
"product": line.product_id.code,
"name": line.product_id.name,
"counted": float(line.new_qty),
"expected": float(line.prev_qty),
"variance": float(variance),
"status": status,
"color": color,
"icon": icon
})
return mobile_lines
# Mobile UI applies color coding
lines_display = get_mobile_lines_with_status(count_id)
Use Case 3: Mobile Quick Adjust¶
# Quick +/- buttons on mobile
def mobile_quick_adjust(line_id, adjustment):
"""
Quick increment/decrement from mobile
adjustment: +1, -1, +10, etc.
"""
line = get_model("stock.count.line").browse(line_id)
# Calculate new quantity
new_qty = max(0, (line.new_qty or 0) + adjustment)
# Update
line.write({"new_qty": new_qty})
# Mobile feedback
return {
"success": True,
"new_qty": float(new_qty),
"adjustment": adjustment,
"feedback": f"{'↑' if adjustment > 0 else '↓'} {abs(adjustment)}"
}
# Mobile UI buttons
# [+10] [+1] [ 5 ] [-1] [-10]
result = mobile_quick_adjust(line_id, +1) # Increment by 1
Use Case 4: Mobile Barcode Line Lookup¶
# Find line by barcode scan
def mobile_find_line_by_barcode(count_id, barcode):
"""
Locate count line by barcode for editing
"""
# Lookup lot by barcode
lot_ids = get_model("stock.lot").search([
["number", "=", barcode]
])
if not lot_ids:
return {"error": "Barcode not found"}
lot_id = lot_ids[0]
lot = get_model("stock.lot").browse(lot_id)
# Find line in count
line_ids = get_model("stock.count.line").search([
["count_id", "=", count_id],
["lot_id", "=", lot_id]
])
if not line_ids:
return {"error": "Item not in this count"}
# Return mobile-formatted line
line_id = line_ids[0]
return {
"found": True,
"line": get_mobile_line_data(line_id),
"action": "show_detail" # Mobile shows detail form
}
# Mobile scan handler
result = mobile_find_line_by_barcode(count_id, "LOT-2024-001")
Use Case 5: Mobile Summary Stats¶
# Mobile count progress display
def get_mobile_count_stats(count_id):
"""Calculate mobile-friendly count statistics"""
lines = get_model("stock.count.line").search_browse([
["count_id", "=", count_id]
])
total_lines = len(lines)
counted = sum(1 for l in lines if l.new_qty != 0)
matches = sum(1 for l in lines if l.new_qty == l.prev_qty)
variances = total_lines - matches
total_qty = sum(l.new_qty or 0 for l in lines)
expected_qty = sum(l.prev_qty or 0 for l in lines)
return {
"progress": {
"total": total_lines,
"counted": counted,
"remaining": total_lines - counted,
"percent": f"{counted/total_lines*100:.0f}%" if total_lines else "0%"
},
"accuracy": {
"matches": matches,
"variances": variances,
"accuracy_pct": f"{matches/total_lines*100:.1f}%" if total_lines else "100%"
},
"quantities": {
"counted": float(total_qty),
"expected": float(expected_qty),
"variance": float(total_qty - expected_qty)
}
}
# Mobile dashboard display
stats = get_mobile_count_stats(count_id)
# Shows: "Progress: 47/100 (47%)"
# "Accuracy: 95.7%"
# "Qty Variance: -23"
Mobile UI Best Practices¶
1. Progressive Disclosure¶
# Don't show all fields at once on mobile
# Good: Show essential fields in list
mobile_list_fields = ["product_id", "new_qty", "prev_qty"]
# Show details only when tapped
mobile_detail_fields = [
"product_id", "lot_id", "bin_location",
"prev_qty", "new_qty", "uom_id",
"prev_cost_price", "unit_price"
]
# Bad: Showing all fields in cramped mobile list
2. Touch-Friendly Input¶
# Large, easy-to-tap controls
# Good: Large numeric input for quantities
mobile_input_config = {
"type": "number",
"keyboard": "numeric",
"size": "large", # Bigger input field
"buttons": ["+10", "+1", "-1", "-10"] # Quick adjust
}
# Bad: Small text input requiring precise tapping
3. Visual Feedback¶
# Immediate visual confirmation
def mobile_update_with_feedback(line_id, new_qty):
"""Update with visual feedback"""
line = get_model("stock.count.line").browse(line_id)
line.write({"new_qty": new_qty})
return {
"success": True,
"animation": "fade_green", # Success animation
"sound": "beep_success",
"haptic": "light", # Vibration feedback
"message": f"✓ Updated to {new_qty}"
}
4. Offline Capability¶
# Cache lines for offline editing
class MobileLineCache:
"""Offline cache for count lines"""
def cache_count_lines(self, count_id):
"""Download lines to mobile device"""
lines = get_model("stock.count.line").search_browse([
["count_id", "=", count_id]
])
# Minimal data for offline storage
cached = []
for line in lines:
cached.append({
"id": line.id,
"product_id": line.product_id.id,
"product_code": line.product_id.code,
"product_name": line.product_id.name,
"lot_id": line.lot_id.id if line.lot_id else None,
"prev_qty": float(line.prev_qty or 0),
"new_qty": float(line.new_qty or 0),
"uom": line.uom_id.name
})
return cached
def sync_offline_changes(self, count_id, offline_updates):
"""Sync changes when back online"""
for update in offline_updates:
line = get_model("stock.count.line").browse(update["id"])
line.write({"new_qty": update["new_qty"]})
Performance Optimization for Mobile¶
1. Pagination¶
# Load lines in pages for mobile scrolling
PAGE_SIZE = 50 # Mobile-friendly page size
def load_mobile_page(count_id, page=1):
"""Load one page of lines"""
offset = (page - 1) * PAGE_SIZE
lines = get_model("stock.count.line").search_browse([
["count_id", "=", count_id]
], limit=PAGE_SIZE, offset=offset)
return [get_mobile_line_data(l.id) for l in lines]
2. Lazy Loading¶
# Load details only when needed
# Initial load: minimal data
initial_data = {
"id": line.id,
"product": line.product_id.code,
"qty": line.new_qty
}
# Detail load: full data (when user taps)
detail_data = get_mobile_line_data(line.id)
3. Delta Sync¶
# Only sync changed lines
def get_changed_lines(count_id, since_timestamp):
"""Get lines modified since timestamp"""
# Track which lines have been updated
# Only sync those lines to save bandwidth
pass
Troubleshooting¶
Lines Not Appearing on Mobile¶
Cause: Pagination or filter issue.
Solution: Verify query parameters:
lines = get_model("stock.count.line").search([
["count_id", "=", count_id]
])
print(f"Total lines: {len(lines)}")
Mobile Update Not Saving¶
Cause: Count not in draft state or validation error.
Solution: Check state before update:
line = get_model("stock.count.line").browse(line_id)
if line.count_id.state != "draft":
return {"error": "Count is completed"}
Slow Mobile Performance¶
Cause: Loading too much data at once.
Solution: Implement pagination:
Related Models¶
| Model | Relationship | Description |
|---|---|---|
stock.count |
Many2One | Parent count (desktop) |
mobile.count |
Many2One | Parent count (mobile) |
product |
Many2One | Product being counted |
stock.lot |
Many2One | Lot/serial number |
uom |
Many2One | Unit of measure |
cycle.stock.count |
Many2One | Cycle count program |
Version History¶
Last Updated: 2024-10-27
Model Version: stock_count_mobile_line.py
Framework: Netforce
Additional Resources¶
- Mobile Count Documentation:
mobile.count - Stock Count Line Documentation:
stock.count.line - Mobile API Best Practices
- Offline Data Synchronization Guide
This documentation is generated for developer onboarding and reference purposes.