- Plate lookup: new plate_vehicles table (v1.7 migration), plate_lookup service with Mexican plate validation, GET/POST endpoints on catalog_bp, plate search UI in catalog vehicle selector - Translations: extend PART_TRANSLATIONS from ~80 to 326 entries covering brake, engine, fuel, cooling, electrical, drivetrain, suspension, steering, exhaust, A/C, lighting, body, interior, fluids, and category translations - Bulk images: image_scraper service with download+resize+placeholder generation, bulk-images and auto-image endpoints on inventory_bp Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,17 @@ from services.audit import log_action
|
||||
inventory_bp = Blueprint('inventory', __name__, url_prefix='/pos/api/inventory')
|
||||
|
||||
|
||||
# ─── AI Classification ───────────────────────────
|
||||
|
||||
@inventory_bp.route('/classify/<part_number>', methods=['GET'])
|
||||
@require_auth('inventory.create')
|
||||
def classify_part_endpoint(part_number):
|
||||
"""Ask AI to identify a part by its OEM number."""
|
||||
from services.ai_chat import classify_part
|
||||
result = classify_part(part_number)
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
# ─── Item CRUD ──────────────────────────────────
|
||||
|
||||
@inventory_bp.route('/items', methods=['GET'])
|
||||
@@ -457,6 +468,80 @@ def delete_image(item_id):
|
||||
return jsonify({'message': 'Image deleted'})
|
||||
|
||||
|
||||
# ─── Bulk Image Import ─────────────────────────
|
||||
|
||||
@inventory_bp.route('/bulk-images', methods=['POST'])
|
||||
@require_auth('inventory.edit')
|
||||
def bulk_upload_images():
|
||||
"""Bulk import images from URLs for multiple inventory items.
|
||||
|
||||
Accepts JSON: {items: [{part_number, image_url}, ...]}
|
||||
Downloads each image, resizes/optimizes, saves to disk, updates DB.
|
||||
Returns {imported: N, errors: [...]}
|
||||
"""
|
||||
data = request.get_json() or {}
|
||||
items_list = data.get('items', [])
|
||||
|
||||
if not items_list:
|
||||
return jsonify({'error': 'items array required'}), 400
|
||||
|
||||
if len(items_list) > 500:
|
||||
return jsonify({'error': 'Maximum 500 items per request'}), 400
|
||||
|
||||
from services.image_scraper import bulk_import
|
||||
|
||||
conn = get_tenant_conn(g.tenant_id)
|
||||
try:
|
||||
result = bulk_import(conn, g.tenant_id, items_list)
|
||||
log_action(conn, 'BULK_IMAGE_IMPORT', 'inventory', None,
|
||||
new_value={'imported': result['imported'], 'error_count': len(result['errors'])})
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
@inventory_bp.route('/items/<int:item_id>/auto-image', methods=['POST'])
|
||||
@require_auth('inventory.edit')
|
||||
def auto_image(item_id):
|
||||
"""Generate a placeholder image for an inventory item.
|
||||
|
||||
Creates a branded placeholder with the part number text.
|
||||
Useful when no real product image is available.
|
||||
"""
|
||||
conn = get_tenant_conn(g.tenant_id)
|
||||
cur = conn.cursor()
|
||||
|
||||
cur.execute("SELECT part_number, name FROM inventory WHERE id = %s", (item_id,))
|
||||
row = cur.fetchone()
|
||||
if not row:
|
||||
cur.close(); conn.close()
|
||||
return jsonify({'error': 'Item not found'}), 404
|
||||
|
||||
part_number, name = row
|
||||
|
||||
try:
|
||||
from services.image_scraper import generate_placeholder
|
||||
rel_url = generate_placeholder(g.tenant_id, item_id, part_number, name or '')
|
||||
|
||||
cur.execute("UPDATE inventory SET image_url = %s WHERE id = %s", (rel_url, item_id))
|
||||
conn.commit()
|
||||
|
||||
log_action(conn, 'AUTO_IMAGE_GENERATED', 'inventory', item_id,
|
||||
new_value={'image_url': rel_url})
|
||||
|
||||
cur.close(); conn.close()
|
||||
return jsonify({
|
||||
'image_url': rel_url,
|
||||
'message': 'Placeholder image generated'
|
||||
})
|
||||
except Exception as e:
|
||||
conn.rollback()
|
||||
cur.close(); conn.close()
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
# ─── Stock Operations ──────────────────────────
|
||||
|
||||
@inventory_bp.route('/purchase', methods=['POST'])
|
||||
|
||||
Reference in New Issue
Block a user