feat(pos): sistema de imagenes — upload, thumbnail, display en inventario y catalogo

- Add POST/DELETE /items/{id}/image endpoints with Pillow processing (resize 800px, thumbnail 300px, JPEG 85%)
- Validate file type (jpg/png/webp) and size (max 5MB)
- Show image in product detail modal with upload/delete buttons
- Enrich catalog parts list with local inventory image when available
- Image directory created automatically on first upload (pos/static/images/parts/)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 02:34:01 +00:00
parent 9702cdc9cc
commit 840790a4d0
3 changed files with 210 additions and 3 deletions

View File

@@ -271,12 +271,14 @@ def get_parts(master_conn, mye_id, group_id, tenant_conn, branch_id, page=1, per
part_id = r[0]
oem = r[1]
local = local_map.get(oem) or local_map.get(f'cat:{part_id}')
# Prefer local inventory image over catalog image
image_url = (local.get('image_url') if local else None) or r[6]
items.append({
'id_part': part_id,
'oem_part_number': oem,
'name': r[3] or r[2], # prefer Spanish name
'description': r[5] or r[4],
'image_url': r[6],
'image_url': image_url,
'local_stock': local['stock'] if local else 0,
'local_price': local['price_1'] if local else None,
'bodega_count': bodega_map.get(part_id, 0),
@@ -558,7 +560,8 @@ def _get_local_stock_bulk(tenant_conn, branch_id, oem_numbers, catalog_part_ids)
cur.execute(f"""
SELECT i.id, i.part_number, i.catalog_part_id,
i.price_1, i.price_2, i.price_3, i.cost, i.tax_rate,
COALESCE(SUM(io.quantity), 0) AS stock
COALESCE(SUM(io.quantity), 0) AS stock,
i.image_url
FROM inventory i
LEFT JOIN inventory_operations io ON io.inventory_id = i.id
WHERE ({where}) AND i.is_active = true{branch_filter}
@@ -577,6 +580,7 @@ def _get_local_stock_bulk(tenant_conn, branch_id, oem_numbers, catalog_part_ids)
'cost': float(r[6]) if r[6] else 0,
'tax_rate': float(r[7]) if r[7] else 0.16,
'stock': r[8],
'image_url': r[9],
}
if r[1]:
result[r[1]] = entry
@@ -598,7 +602,8 @@ def _get_local_stock_single(tenant_conn, branch_id, oem_part_number, catalog_par
cur.execute(f"""
SELECT i.id, i.price_1, i.price_2, i.price_3, i.cost, i.tax_rate,
i.location, i.unit, i.barcode,
COALESCE(SUM(io.quantity), 0) AS stock
COALESCE(SUM(io.quantity), 0) AS stock,
i.image_url
FROM inventory i
LEFT JOIN inventory_operations io ON io.inventory_id = i.id
WHERE (i.part_number = %s OR i.catalog_part_id = %s)
@@ -624,6 +629,7 @@ def _get_local_stock_single(tenant_conn, branch_id, oem_part_number, catalog_par
'unit': row[7] or 'PZA',
'barcode': row[8],
'stock': row[9],
'image_url': row[10],
}