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:
2025-10-28 16:31:48 +07:00
commit ab0d1ae0db
9 changed files with 1971 additions and 0 deletions

196
FIXES_APPLIED.md Normal file
View 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