Files
Autoparts-DB/pos/blueprints/image_bp.py
Nexus Dev 9ff3dc4c8b FASE 4-5-6: Infraestructura, CRM, Service Orders, Notificaciones, Ahorro, Logistica, API Publica
FASE 4:
- Redis cache de stock con fallback graceful
- Multi-moneda (MXN/USD) con contabilidad en MXN
- Proveedores y ordenes de compra completo
- Meilisearch 1.5M+ partes indexadas
- Metabase KPIs con dashboard auto-generado

FASE 5:
- CRM mejorado: activities, tags, loyalty program, analytics
- Imagenes de partes: upload, resize, thumbnails WebP
- Ordenes de servicio Kanban: received->diagnosis->repair->ready->delivered
- Garantias/RMA, alertas de reorden, multi-sucursal
- Stubs BNPL (APLAZO) y ERP Sync (Aspel/Contpaqi)

FASE 6:
- Notificaciones automaticas: push/WhatsApp/email/in-app
- Reportes de ahorro vs retail_price
- Logistica + tracking: DHL, FedEx, Estafeta, 99min, Uber
- API Publica: API keys, rate limiting, catalog search

Migraciones: v1.9-v3.0
Tests: 93/93 pasando
Backup: nexus_backup_20260427_045859.tar.gz
2026-04-27 05:23:30 +00:00

137 lines
4.4 KiB
Python

"""Image Blueprint: part image upload and management.
Prefix: /pos/api/inventory
"""
from flask import Blueprint, request, jsonify, g, send_from_directory
from middleware import require_auth
from tenant_db import get_tenant_conn
from services.image_service import save_image, delete_image, get_image_info
import os
image_bp = Blueprint('images', __name__, url_prefix='/pos/api/inventory')
@image_bp.route('/items/<int:item_id>/image', methods=['POST'])
@require_auth()
def upload_item_image(item_id):
"""Upload an image for an inventory item.
Supports multipart/form-data with 'image' file, or JSON with 'image_url'.
"""
tenant_id = g.tenant_id
# Check if item exists
conn = get_tenant_conn(tenant_id)
try:
cur = conn.cursor()
cur.execute("SELECT id FROM inventory WHERE id = %s", (item_id,))
if not cur.fetchone():
cur.close()
return jsonify({'error': 'Inventory item not found'}), 404
cur.close()
file_obj = None
image_url = None
filename_hint = None
if 'image' in request.files:
file = request.files['image']
if file.filename:
file_obj = file.stream
filename_hint = file.filename
elif request.is_json:
data = request.get_json() or {}
image_url = data.get('image_url')
if not file_obj and not image_url:
return jsonify({'error': 'No image provided. Upload via multipart "image" field or JSON "image_url"'}), 400
result = save_image(tenant_id, item_id, file_obj=file_obj,
image_url=image_url, filename_hint=filename_hint)
# Update inventory.image_url
cur = conn.cursor()
cur.execute("""
UPDATE inventory SET image_url = %s WHERE id = %s
""", (result['image_url'], item_id))
conn.commit()
cur.close()
return jsonify(result), 201
finally:
conn.close()
@image_bp.route('/items/<int:item_id>/image', methods=['GET'])
@require_auth()
def get_item_image(item_id):
"""Get image info for an inventory item."""
tenant_id = g.tenant_id
info = get_image_info(tenant_id, item_id)
return jsonify(info)
@image_bp.route('/items/<int:item_id>/image', methods=['DELETE'])
@require_auth()
def delete_item_image(item_id):
"""Delete the image for an inventory item."""
tenant_id = g.tenant_id
result = delete_image(tenant_id, item_id)
conn = get_tenant_conn(tenant_id)
try:
cur = conn.cursor()
cur.execute("UPDATE inventory SET image_url = NULL WHERE id = %s", (item_id,))
conn.commit()
cur.close()
return jsonify({'message': 'Image deleted', 'deleted': result['deleted']})
finally:
conn.close()
@image_bp.route('/images/bulk', methods=['POST'])
@require_auth()
def bulk_import_images():
"""Bulk import images from a list of {item_id, image_url} objects.
Body: {"items": [{"item_id": 1, "image_url": "https://..."}, ...]}
"""
data = request.get_json() or {}
items = data.get('items', [])
if not items:
return jsonify({'error': 'items array is required'}), 400
tenant_id = g.tenant_id
results = {'successful': [], 'failed': []}
conn = get_tenant_conn(tenant_id)
try:
cur = conn.cursor()
for item in items:
item_id = item.get('item_id')
image_url = item.get('image_url')
if not item_id or not image_url:
results['failed'].append({'item_id': item_id, 'error': 'Missing item_id or image_url'})
continue
# Verify item exists
cur.execute("SELECT id FROM inventory WHERE id = %s", (item_id,))
if not cur.fetchone():
results['failed'].append({'item_id': item_id, 'error': 'Item not found'})
continue
try:
result = save_image(tenant_id, item_id, image_url=image_url)
cur.execute("UPDATE inventory SET image_url = %s WHERE id = %s",
(result['image_url'], item_id))
results['successful'].append({'item_id': item_id, 'image_url': result['image_url']})
except Exception as e:
results['failed'].append({'item_id': item_id, 'error': str(e)})
conn.commit()
cur.close()
return jsonify(results)
finally:
conn.close()