Add quote bridge and quote handoff flow

This commit is contained in:
2026-06-29 18:41:45 -05:00
parent be1475dd8e
commit a329168765
17 changed files with 1707 additions and 509 deletions
+104
View File
@@ -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"""