Add quote bridge and quote handoff flow
This commit is contained in:
@@ -6,6 +6,10 @@ from flask import Blueprint, render_template, send_from_directory, session, json
|
||||
from blueprints.auth import login_required, get_current_user, can_user
|
||||
import data_access as da
|
||||
import os
|
||||
import json
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
import config
|
||||
|
||||
products_bp = Blueprint('products', __name__, url_prefix='/quiz')
|
||||
|
||||
@@ -226,6 +230,106 @@ def get_product_attributes():
|
||||
'attributes': attributes
|
||||
})
|
||||
|
||||
@products_bp.route('/api/submit-quote', methods=['POST'])
|
||||
@login_required
|
||||
def submit_quote():
|
||||
"""Submit current order items as a handoff file for downstream processing"""
|
||||
user = get_current_user()
|
||||
if not user:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'Not logged in'
|
||||
}), 401
|
||||
|
||||
# Allow explicit quote permission or super admin
|
||||
if not can_user('create_quotes') and not user.get('superAdmin', False):
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'Permission denied'
|
||||
}), 403
|
||||
|
||||
try:
|
||||
payload = request.get_json(silent=True) or {}
|
||||
items = payload.get('items', [])
|
||||
|
||||
if not isinstance(items, list) or len(items) == 0:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'No order items to submit'
|
||||
}), 400
|
||||
|
||||
# Normalize each item to a safe subset for handoff
|
||||
normalized_items = []
|
||||
for item in items:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
normalized_items.append({
|
||||
'productCode': str(item.get('productCode', '')).strip(),
|
||||
'description': str(item.get('description', '')).strip(),
|
||||
'material': str(item.get('material', '')).strip(),
|
||||
'color': str(item.get('color', '')).strip(),
|
||||
'size': str(item.get('size', '')).strip(),
|
||||
'hingeLocation': str(item.get('hingeLocation', '')).strip(),
|
||||
'quantity': int(item.get('quantity', 1) or 1)
|
||||
})
|
||||
|
||||
normalized_items = [i for i in normalized_items if i['productCode']]
|
||||
if not normalized_items:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'Order items are invalid'
|
||||
}), 400
|
||||
|
||||
submission_id = f"{datetime.now(timezone.utc).strftime('%Y%m%d-%H%M%S')}-{uuid.uuid4().hex[:8]}"
|
||||
created_at = datetime.now(timezone.utc).isoformat()
|
||||
|
||||
quote_data = {
|
||||
'schemaVersion': 1,
|
||||
'submissionId': submission_id,
|
||||
'createdAt': created_at,
|
||||
'createdBy': {
|
||||
'username': session.get('username'),
|
||||
'location': session.get('currentLocation'),
|
||||
'accessibleLocations': session.get('accessibleLocations', [])
|
||||
},
|
||||
'source': {
|
||||
'app': 'product-finder',
|
||||
'route': payload.get('route', ''),
|
||||
'url': payload.get('url', '')
|
||||
},
|
||||
'context': {
|
||||
'answers': payload.get('answers', {}),
|
||||
'bitValue': payload.get('bitValue', 0)
|
||||
},
|
||||
'items': normalized_items
|
||||
}
|
||||
|
||||
outgoing_dir = config.QUOTE_OUTGOING_DIR
|
||||
os.makedirs(outgoing_dir, exist_ok=True)
|
||||
|
||||
final_filename = f"{submission_id}.json"
|
||||
final_path = os.path.join(outgoing_dir, final_filename)
|
||||
tmp_path = final_path + '.tmp'
|
||||
|
||||
# Atomic handoff write
|
||||
with open(tmp_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(quote_data, f, indent=2)
|
||||
os.replace(tmp_path, final_path)
|
||||
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'message': 'Quote submitted successfully',
|
||||
'submissionId': submission_id,
|
||||
'fileName': final_filename,
|
||||
'filePath': final_path
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': str(e)
|
||||
}), 500
|
||||
|
||||
@products_bp.route('/css/<path:filename>')
|
||||
def serve_css(filename):
|
||||
"""Serve CSS files"""
|
||||
|
||||
Reference in New Issue
Block a user