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.
This commit is contained in:
196
FIXES_APPLIED.md
Normal file
196
FIXES_APPLIED.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# 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:**
|
||||
```python
|
||||
r.raise_for_status()
|
||||
sid = r.json().get('pk', r.json().get('id')) # ❌ Fails if response is list
|
||||
```
|
||||
|
||||
**After:**
|
||||
```python
|
||||
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:**
|
||||
```python
|
||||
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:**
|
||||
```python
|
||||
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
|
||||
Reference in New Issue
Block a user