Add Daily Mirror interactive dashboard with charts and pivot tables

- Created comprehensive dashboard with Chart.js visualizations
- Added API endpoint /api/dashboard_data for aggregated data
- Implemented weekly tracking for orders, production, and deliveries
- Added interactive pivot table for customer × week analysis
- Fixed collation issues in database joins
- Includes 4 summary cards with key metrics
- Charts display orders, production finished, and deliveries by week
- Click-to-expand data tables for detailed view
- Time range filter (4-52 weeks)
- Data sources: scanfg_orders (finished), dm_orders, dm_deliveries
This commit is contained in:
ske087
2025-11-25 00:09:19 +02:00
parent c6e254c390
commit c38b5d7b44
2 changed files with 1124 additions and 414 deletions

View File

@@ -568,6 +568,175 @@ def api_daily_mirror_history_data():
return jsonify({'error': str(e)}), 500
@daily_mirror_bp.route('/api/dashboard_data', methods=['GET'])
def api_dashboard_data():
"""API endpoint to get aggregated dashboard data for pivot table view"""
access_check = check_daily_mirror_api_access()
if access_check:
return access_check
try:
print(f"DEBUG: dashboard_data API called")
from datetime import datetime, timedelta
# Get date range parameters
weeks_back = int(request.args.get('weeks_back', 12))
end_date = datetime.now()
start_date = end_date - timedelta(weeks=weeks_back)
dm_db = DailyMirrorDatabase()
if not dm_db.connect():
return jsonify({'error': 'Database connection failed'}), 500
cursor = dm_db.connection.cursor()
# 1. Get finished orders from scanfg_orders (production finished)
cursor.execute("""
SELECT
DATE_FORMAT(date, '%%Y%%u') as week_number,
DATE_FORMAT(date, '%%Y-W%%u') as week_display,
COUNT(*) as finished_count,
SUM(approved_quantity) as approved_qty,
SUM(rejected_quantity) as rejected_qty
FROM scanfg_orders
WHERE date >= %s AND date <= %s
GROUP BY week_number, week_display
ORDER BY week_number
""", (start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d')))
finished_orders_raw = cursor.fetchall()
finished_orders = [
{
'week_number': row[0],
'week_display': row[1],
'finished_count': row[2],
'approved_qty': row[3],
'rejected_qty': row[4]
}
for row in finished_orders_raw
]
# 2. Get customer orders from dm_orders by customer and week
cursor.execute("""
SELECT
c.customer_name,
DATE_FORMAT(o.order_date, '%%Y%%u') as week_number,
DATE_FORMAT(o.order_date, '%%Y-W%%u') as week_display,
COUNT(*) as order_count,
SUM(o.quantity_requested) as total_quantity,
SUM(CASE WHEN o.order_status = 'OPEN' THEN o.quantity_requested ELSE 0 END) as open_quantity,
SUM(CASE WHEN o.order_status = 'COMPLETED' THEN o.quantity_requested ELSE 0 END) as completed_quantity
FROM dm_orders o
LEFT JOIN dm_customers c ON o.customer_code COLLATE utf8mb4_unicode_ci = c.customer_code COLLATE utf8mb4_unicode_ci
WHERE o.order_date >= %s AND o.order_date <= %s
GROUP BY c.customer_name, week_number, week_display
ORDER BY c.customer_name, week_number
""", (start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d')))
customer_orders_raw = cursor.fetchall()
customer_orders = [
{
'customer_name': row[0],
'week_number': row[1],
'week_display': row[2],
'order_count': row[3],
'total_quantity': row[4],
'open_quantity': row[5],
'completed_quantity': row[6]
}
for row in customer_orders_raw
]
# 3. Get deliveries from dm_deliveries by customer and week
cursor.execute("""
SELECT
c.customer_name,
DATE_FORMAT(d.delivery_date, '%%Y%%u') as week_number,
DATE_FORMAT(d.delivery_date, '%%Y-W%%u') as week_display,
COUNT(*) as delivery_count,
SUM(d.quantity_delivered) as total_delivered
FROM dm_deliveries d
LEFT JOIN dm_customers c ON d.customer_code COLLATE utf8mb4_unicode_ci = c.customer_code COLLATE utf8mb4_unicode_ci
WHERE d.delivery_date >= %s AND d.delivery_date <= %s
GROUP BY c.customer_name, week_number, week_display
ORDER BY c.customer_name, week_number
""", (start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d')))
deliveries_raw = cursor.fetchall()
deliveries = [
{
'customer_name': row[0],
'week_number': row[1],
'week_display': row[2],
'delivery_count': row[3],
'total_delivered': row[4]
}
for row in deliveries_raw
]
# 4. Get summary metrics
cursor.execute("""
SELECT
COUNT(*) as total_orders,
SUM(quantity_requested) as total_quantity,
COUNT(DISTINCT customer_code) as unique_customers
FROM dm_orders
WHERE order_date >= %s AND order_date <= %s
""", (start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d')))
order_summary_row = cursor.fetchone()
cursor.execute("""
SELECT
COUNT(*) as total_finished,
SUM(approved_quantity) as total_approved,
SUM(rejected_quantity) as total_rejected
FROM scanfg_orders
WHERE date >= %s AND date <= %s
""", (start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d')))
production_summary_row = cursor.fetchone()
cursor.execute("""
SELECT
COUNT(*) as total_deliveries,
SUM(quantity_delivered) as total_delivered
FROM dm_deliveries
WHERE delivery_date >= %s AND delivery_date <= %s
""", (start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d')))
delivery_summary_row = cursor.fetchone()
dm_db.disconnect()
print(f"DEBUG: Data aggregation successful. Orders: {len(customer_orders)}, Production: {len(finished_orders)}, Deliveries: {len(deliveries)}")
# Build response
response_data = {
'date_range': {
'start': start_date.strftime('%Y-%m-%d'),
'end': end_date.strftime('%Y-%m-%d'),
'weeks_back': weeks_back
},
'summary': {
'total_orders': order_summary_row[0] or 0,
'total_quantity': int(order_summary_row[1] or 0),
'unique_customers': order_summary_row[2] or 0,
'total_finished': production_summary_row[0] or 0,
'total_approved': int(production_summary_row[1] or 0),
'total_rejected': int(production_summary_row[2] or 0),
'total_deliveries': delivery_summary_row[0] or 0,
'total_delivered': int(delivery_summary_row[1] or 0)
},
'finished_orders_by_week': finished_orders,
'customer_orders_by_week': customer_orders,
'deliveries_by_week': deliveries
}
print(f"DEBUG: Returning response data successfully")
return jsonify(response_data)
except Exception as e:
print(f"ERROR getting dashboard data: {e}")
import traceback
traceback.print_exc()
return jsonify({'error': str(e)}), 500
# =============================================
# TUNE DATABASE ROUTES
# =============================================
@@ -1174,4 +1343,4 @@ def clear_delivery():
except Exception as e:
print(f"Error clearing delivery records: {e}")
return jsonify({'error': str(e)}), 500
return jsonify({'error': str(e)}), 500

File diff suppressed because it is too large Load Diff