fix: Add support for ANSI MH10.8.2 barcode format
Fixes barcode parsing for ANSI MH10.8.2 format barcodes that don't use GS/RS separators. Problem: - Barcodes like [)>06PSAM9019-ND1PJL-100-25-T... were not being parsed - Only separator-based and JSON formats were supported - User's real-world barcodes were being added to queue as raw strings Solution: - Added ANSI MH10.8.2 format detection ([)>06 prefix) - Extract part code between P and first field marker (1P, 30P) - Extract quantity from Q<digits> pattern - Updated both desktop and web app parsing logic Tested with real barcode: - Input: [)>06PSAM9019-ND1PJL-100-25-T30PSAM9019-NDK1...Q1811... - Parsed: Part=SAM9019-ND, Qty=1811 ✅ Files Changed: - src/stocktool/stock_tool_gui_v2.py - Enhanced parse_scan() - src/stocktool/web/static/js/app.js - Enhanced parseBarcode() - test_barcode_parsing.py - Test script for validation - test_barcode_analyze.py - Barcode structure analysis tool - QUICKSTART_WEB.md - Quick start guide for web app Supported Formats Now: 1. JSON-like: {PM:PART-CODE,QTY:10} 2. Separator-based: GS/RS (\x1D, \x1E) separated fields 3. ANSI MH10.8.2: [)>06P<part>...Q<qty>... (NEW!) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -416,6 +416,11 @@ def parse_scan(raw: str) -> Tuple[Optional[str], Optional[int]]:
|
||||
"""
|
||||
Parse scanned barcode to extract part code and quantity.
|
||||
|
||||
Supports multiple formats:
|
||||
- JSON-like: {PM:PART-CODE,QTY:10}
|
||||
- Separator-based: Fields separated by GS/RS (\x1D, \x1E)
|
||||
- ANSI MH10.8.2: [)>06P<part>...Q<qty>...
|
||||
|
||||
Args:
|
||||
raw: Raw barcode string
|
||||
|
||||
@@ -451,6 +456,36 @@ def parse_scan(raw: str) -> Tuple[Optional[str], Optional[int]]:
|
||||
pass
|
||||
return part, qty
|
||||
|
||||
# Handle ANSI MH10.8.2 format: [)>06P<part>...
|
||||
if raw.startswith('[)>06'):
|
||||
part = None
|
||||
qty = None
|
||||
|
||||
# Extract part number - it's after [)>06P and before the next field marker
|
||||
# Look for common field markers: 1P, 30P (these are explicit markers)
|
||||
if len(raw) > 6 and raw[5] == 'P':
|
||||
after_p = raw[6:]
|
||||
|
||||
# Find the next explicit field marker
|
||||
markers_to_find = ['1P', '30P']
|
||||
end_idx = len(after_p)
|
||||
|
||||
for marker in markers_to_find:
|
||||
idx = after_p.find(marker)
|
||||
if idx != -1 and idx < end_idx:
|
||||
end_idx = idx
|
||||
|
||||
part = clean_part_code(after_p[:end_idx])
|
||||
|
||||
# Extract quantity after Q
|
||||
if 'Q' in raw:
|
||||
q_matches = re.findall(r'Q(\d+)', raw)
|
||||
if q_matches:
|
||||
qty = int(q_matches[0])
|
||||
|
||||
if part or qty:
|
||||
return part, qty
|
||||
|
||||
# Handle separator-based format
|
||||
part = None
|
||||
qty = None
|
||||
|
||||
@@ -205,19 +205,94 @@ function stockApp() {
|
||||
|
||||
/**
|
||||
* Parse barcode input
|
||||
*
|
||||
* Supports multiple formats:
|
||||
* - JSON-like: {PM:PART-CODE,QTY:10}
|
||||
* - Separator-based: Fields separated by GS/RS (\x1D, \x1E)
|
||||
* - ANSI MH10.8.2: [)>06P<part>...Q<qty>...
|
||||
*/
|
||||
async parseBarcode(raw) {
|
||||
// Simple parsing - can be enhanced
|
||||
let part_code = raw;
|
||||
let part_code = null;
|
||||
let quantity = null;
|
||||
|
||||
// Check for JSON-like format
|
||||
if (raw.startsWith('{') && raw.includes('}')) {
|
||||
const match = raw.match(/pm:([^,}]+)/i);
|
||||
if (match) part_code = match[1];
|
||||
/**
|
||||
* Clean part code by removing non-ASCII characters
|
||||
*/
|
||||
const cleanPartCode = (code) => {
|
||||
// Keep only ASCII printable characters (32-126)
|
||||
return code.split('').filter(char => {
|
||||
const charCode = char.charCodeAt(0);
|
||||
return charCode >= 32 && charCode <= 126;
|
||||
}).join('').trim();
|
||||
};
|
||||
|
||||
const qtyMatch = raw.match(/qty:(\d+)/i);
|
||||
if (qtyMatch) quantity = parseInt(qtyMatch[1]);
|
||||
// Handle JSON-like format: {PM:PART-CODE,QTY:10}
|
||||
if (raw.startsWith('{') && raw.includes('}')) {
|
||||
const content = raw.trim().slice(1, -1);
|
||||
|
||||
for (const kv of content.split(',')) {
|
||||
if (!kv.includes(':')) continue;
|
||||
|
||||
const [k, v] = kv.split(':', 2);
|
||||
const key = k.trim().toUpperCase();
|
||||
const val = v.trim();
|
||||
|
||||
if (key === 'PM') {
|
||||
part_code = cleanPartCode(val);
|
||||
} else if (key === 'QTY') {
|
||||
const parsed = parseInt(val);
|
||||
if (!isNaN(parsed)) quantity = parsed;
|
||||
}
|
||||
}
|
||||
return { part_code, quantity };
|
||||
}
|
||||
|
||||
// Handle ANSI MH10.8.2 format: [)>06P<part>...
|
||||
if (raw.startsWith('[)>06')) {
|
||||
// Extract part number - it's after [)>06P and before the next field marker
|
||||
if (raw.length > 6 && raw[5] === 'P') {
|
||||
const afterP = raw.substring(6);
|
||||
|
||||
// Find the next explicit field marker (1P or 30P)
|
||||
const markers = ['1P', '30P'];
|
||||
let endIdx = afterP.length;
|
||||
|
||||
for (const marker of markers) {
|
||||
const idx = afterP.indexOf(marker);
|
||||
if (idx !== -1 && idx < endIdx) {
|
||||
endIdx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
part_code = cleanPartCode(afterP.substring(0, endIdx));
|
||||
}
|
||||
|
||||
// Extract quantity after Q
|
||||
const qMatch = raw.match(/Q(\d+)/);
|
||||
if (qMatch) {
|
||||
quantity = parseInt(qMatch[1]);
|
||||
}
|
||||
|
||||
return { part_code, quantity };
|
||||
}
|
||||
|
||||
// Handle separator-based format (GS/RS separators: \x1D or \x1E)
|
||||
const sepRegex = /[\x1D\x1E]/g;
|
||||
const fields = raw.split(sepRegex);
|
||||
|
||||
for (const field of fields) {
|
||||
if (!field) continue;
|
||||
|
||||
// Part code patterns: 30P, 1P
|
||||
if (field.startsWith('30P')) {
|
||||
part_code = cleanPartCode(field.substring(3));
|
||||
} else if (field.toLowerCase().startsWith('1p')) {
|
||||
part_code = cleanPartCode(field.substring(2));
|
||||
}
|
||||
// Quantity pattern: Q followed by digits
|
||||
else if (field.toLowerCase().startsWith('q') && /^\d+$/.test(field.substring(1))) {
|
||||
quantity = parseInt(field.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
return { part_code, quantity };
|
||||
|
||||
Reference in New Issue
Block a user