""" Fixed-Price Agreement Frontend Views """ from fastapi import APIRouter, Request from fastapi.responses import HTMLResponse, JSONResponse from fastapi.templating import Jinja2Templates from app.core.database import execute_query import logging logger = logging.getLogger(__name__) router = APIRouter() templates = Jinja2Templates(directory="app") @router.get("/fixed-price-agreements", response_class=HTMLResponse) async def list_agreements(request: Request): """List all fixed-price agreements""" # Load customers for the create modal try: customers_query = """ SELECT id, name, cvr_number, email, phone, city, is_active FROM customers WHERE deleted_at IS NULL AND is_active = true ORDER BY name LIMIT 1000 """ customers = execute_query(customers_query) logger.info(f"📋 Loaded {len(customers)} customers for modal") except Exception as e: logger.error(f"❌ Error loading customers: {e}") customers = [] return templates.TemplateResponse("fixed_price/frontend/list.html", { "request": request, "customers": customers }) @router.get("/fixed-price-agreements/{agreement_id}", response_class=HTMLResponse) async def agreement_detail(request: Request, agreement_id: int): """Agreement detail page with periods and related sager""" from fastapi import HTTPException try: # Fetch agreement agr_query = """ SELECT a.*, COALESCE(p.used_hours, 0) as current_used_hours, COALESCE(p.remaining_hours, a.monthly_hours) as current_remaining_hours, p.status as current_period_status FROM customer_fixed_price_agreements a LEFT JOIN fixed_price_billing_periods p ON p.agreement_id = a.id AND p.period_start <= CURRENT_DATE AND p.period_end >= CURRENT_DATE WHERE a.id = %s """ agreement = execute_query(agr_query, (agreement_id,)) if not agreement: raise HTTPException(status_code=404, detail="Aftale ikke fundet") agreement = agreement[0] # Fetch all billing periods periods_query = """ SELECT * FROM fixed_price_billing_periods WHERE agreement_id = %s ORDER BY period_start DESC """ periods = execute_query(periods_query, (agreement_id,)) # Fetch related sager sager_query = """ SELECT DISTINCT s.id, s.titel, s.status, s.created_at FROM sag_sager s INNER JOIN tmodule_times t ON t.sag_id = s.id WHERE t.fixed_price_agreement_id = %s AND s.deleted_at IS NULL ORDER BY s.created_at DESC LIMIT 50 """ sager = execute_query(sager_query, (agreement_id,)) # Fetch time entries time_query = """ SELECT t.*, s.titel as sag_titel FROM tmodule_times t LEFT JOIN sag_sager s ON s.id = t.sag_id WHERE t.fixed_price_agreement_id = %s ORDER BY t.created_at DESC LIMIT 100 """ time_entries = execute_query(time_query, (agreement_id,)) return templates.TemplateResponse("fixed_price/frontend/detail.html", { "request": request, "agreement": agreement, "periods": periods, "sager": sager, "time_entries": time_entries }) except Exception as e: logger.error(f"❌ Error loading agreement detail: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.get("/fixed-price-agreements/reports/dashboard", response_class=HTMLResponse) async def reports_dashboard(request: Request): """Reporting dashboard with profitability analysis""" try: # Get summary stats stats_query = """ SELECT COUNT(*) FILTER (WHERE status = 'active') as active_agreements, COUNT(*) as total_agreements, SUM(monthly_hours * hourly_rate) as total_revenue, SUM(monthly_hours * (hourly_rate - 300)) as estimated_profit FROM customer_fixed_price_agreements """ stats = execute_query(stats_query) # Get performance data from view performance_query = """ SELECT *, CASE WHEN total_revenue > 0 THEN (total_profit / total_revenue * 100) ELSE 0 END as profit_margin FROM fixed_price_agreement_performance ORDER BY total_profit DESC LIMIT 50 """ performance = execute_query(performance_query) # Get monthly trends trends_query = """ SELECT *, month as period_month, CASE WHEN monthly_total_revenue > 0 THEN (monthly_profit / monthly_total_revenue * 100) ELSE 0 END as avg_profit_margin FROM fixed_price_monthly_trends ORDER BY month DESC LIMIT 12 """ trends = execute_query(trends_query) # Get customer breakdown customer_query = """ SELECT *, total_hours_used as total_used_hours, CASE WHEN total_revenue > 0 THEN (total_profit / total_revenue * 100) ELSE 0 END as avg_profit_margin FROM fixed_price_customer_summary ORDER BY total_used_hours DESC LIMIT 20 """ customers = execute_query(customer_query) return templates.TemplateResponse("fixed_price/frontend/reports.html", { "request": request, "stats": stats[0] if stats else {}, "performance": performance, "trends": trends, "customers": customers }) except Exception as e: logger.error(f"❌ Error loading reports: {e}") return templates.TemplateResponse("fixed_price/frontend/reports.html", { "request": request, "stats": {}, "performance": [], "trends": [], "customers": [], "error": str(e) }) @router.get("/api/fixed-price-agreements/customers") async def get_customers_for_agreements(): """Get all active customers for fixed-price agreement creation""" try: query = """ SELECT id, name, cvr_number, email, phone, city, is_active FROM customers WHERE deleted_at IS NULL AND is_active = true ORDER BY name LIMIT 1000 """ customers = execute_query(query) logger.info(f"📋 Loaded {len(customers)} customers for fixed-price agreements") return customers except Exception as e: logger.error(f"❌ Error loading customers: {e}") return []