Files
inventree-stock-tool/FIXES_APPLIED.md
grabowski ab0d1ae0db Initial commit: InvenTree Stock Tool v2
A comprehensive barcode scanning application for InvenTree inventory management.

Features:
- Multi-mode operation (Add/Update/Check/Locate stock)
- Smart duplicate prevention when adding stock
- Barcode scanning with automatic part code cleaning
- Real-time server connection monitoring
- Part information display with images
- Debug mode for troubleshooting

Fixes applied:
- Fixed encoding issues with non-ASCII characters in barcodes
- Fixed API response handling for list and dict formats
- Implemented duplicate prevention using PATCH to update existing stock
- Added comprehensive error handling and logging

Includes test suite for verification of all fixes.
2025-10-28 16:31:48 +07:00

6.0 KiB
Raw Permalink Blame History

Stock Tool GUI v2 - Fixes Applied

Summary

Fixed three critical issues in stock_tool_gui_v2.py:

  1. Encoding issue with barcode part numbers
  2. API response handling causing "'list' object has no attribute 'get'" errors
  3. Duplicate stock items being created when adding to existing location

Issue 1: Encoding Problem with Part Numbers

Problem

Raw scan data contained: pm:STHW4-DU-HS24041¡­ The non-ASCII characters (¡­) were being included in the parsed part number.

Root Cause

The parse_scan() function was not cleaning/sanitizing the extracted part codes.

Fix Applied

Added clean_part_code() helper function that:

  • Filters out all non-ASCII characters
  • Keeps only printable ASCII characters (codes 32-126)
  • Removes encoding artifacts like ¡­

Location

File: stock_tool_gui_v2.py Function: parse_scan() (lines 294-348)

Result

  • Before: STHW4-DU-HS24041¡­
  • After: STHW4-DU-HS24041

Issue 2: API Response Handling Error

Problem

Error: 'list' object has no attribute 'get'

  • Stock not being added to InvenTree
  • Stock levels not updating after adding items

Root Cause

The InvenTree API POST response can return either:

  • A dictionary: {'pk': 123, 'quantity': 150, ...}
  • A list: [{'pk': 123, 'quantity': 150, ...}]

The code was assuming it would always be a dict and calling .get() directly on the response, which failed when the API returned a list.

Locations with the Bug

  1. _add_stock() function - line 806 (original)
  2. _update_stock() function - line 865 (original)
  3. Misplaced get_stock_level() function at top of file

Fixes Applied

1. Fixed _add_stock() function (lines 797-827)

Before:

r.raise_for_status()
sid = r.json().get('pk', r.json().get('id'))  # ❌ Fails if response is list

After:

r.raise_for_status()

# Handle response - might be list or dict
response_data = r.json()
if isinstance(response_data, list):
    stock_item = response_data[0] if response_data else {}
else:
    stock_item = response_data

sid = stock_item.get('pk', stock_item.get('id'))  # ✓ Works for both

2. Fixed _update_stock() function (lines 858-875)

Applied the same fix for consistency.

3. Moved get_stock_level() function (lines 256-270)

  • Removed misplaced definition from top of file (before imports)
  • Re-added in correct location after find_stock_item() function

Issue 3: Duplicate Stock Items Created

Problem

When scanning a part that already exists at a storage location, the tool was creating a duplicate stock item instead of adding to the existing quantity.

Example:

  • Location C64_PSU has 150x STHW4-DU-HS24041
  • Scan barcode to add 100 more
  • Bug: Creates second stock item with 100 (now have two separate items)
  • Expected: Updates existing item to 250

Root Cause

The _add_stock() function was always creating a new stock item via POST without checking if one already existed at that location.

Fix Applied (lines 791-845)

Before:

def _add_stock(self, part_id: int, part_code: str, quantity: Optional[int]):
    # Always creates new stock item - WRONG!
    r = requests.post(f"{self.host}/api/stock/", ...)

After:

def _add_stock(self, part_id: int, part_code: str, quantity: Optional[int]):
    # Check if stock item already exists at this location
    existing = find_stock_item(self.host, self.token, part_id, self.current_loc)

    if existing:
        # Stock exists - update the quantity by adding to it
        sid = existing.get('pk', existing.get('id'))
        new_stock = current_stock + quantity
        r = requests.patch(f"{self.host}/api/stock/{sid}/",
                          json={'quantity': new_stock})
        # Updates existing item ✓
    else:
        # No existing stock - create new stock item
        r = requests.post(f"{self.host}/api/stock/", ...)
        # Creates new item ✓

Behavior Now

  • Same part + same location = Updates existing stock item (no duplicate)
  • Same part + different location = Creates new stock item (correct)
  • New part = Creates new stock item (correct)

Testing

Test Files Created

  1. test_parse_fix.py - Verifies encoding fix
  2. test_stock_level.py - Verifies stock level retrieval
  3. test_add_stock.py - Verifies API response handling
  4. test_duplicate_handling.py - Verifies duplicate prevention logic

Test Results

All tests pass ✓


Expected Behavior Now

When scanning barcode:

{pbn:PICK251017100019,on:WM2510170196,pc:C18548292,pm:STHW4-DU-HS24041¡­,qty:150,mc:,cc:1,pdi:180368458,hp:null,wc:JS}

The tool will:

  1. Extract clean part number: STHW4-DU-HS24041
  2. Extract quantity: 150
  3. Check if part already exists at current location
  4. If exists: Add to existing stock (no duplicate created)
  5. If new: Create new stock item
  6. Display updated stock levels
  7. Handle both dict and list API responses correctly

Example Workflows

Scenario 1: First time adding part to location

  • Scan: Part STHW4-DU-HS24041, Qty: 150
  • Result: Creates new stock item
  • Log: ✔ Created new stock item for 'STHW4-DU-HS24041' → StockItem #123
  • Log: 📊 Stock: 0 → 150 (+150)

Scenario 2: Adding more of same part to same location

  • Scan: Part STHW4-DU-HS24041, Qty: 100
  • Result: Updates existing stock item #123
  • Log: ✔ Added 100× to existing 'STHW4-DU-HS24041' (StockItem #123)
  • Log: 📊 Stock: 150 → 250 (+100)
  • No duplicate created!

Scenario 3: Adding same part to different location

  • Change location to C65_PSU
  • Scan: Part STHW4-DU-HS24041, Qty: 75
  • Result: Creates new stock item #124 at the new location
  • Log: ✔ Created new stock item for 'STHW4-DU-HS24041' → StockItem #124
  • Log: 📊 Stock: 0 → 75 (+75)

Files Modified

  • stock_tool_gui_v2.py - Main application file

Files Created

  • test_parse_fix.py - Test for encoding fix
  • test_stock_level.py - Test for stock level retrieval
  • test_add_stock.py - Test for API response handling
  • FIXES_APPLIED.md - This document