""" BMC Office Subscriptions Upload Router Allows admins to upload Excel files with subscription data """ from fastapi import APIRouter, UploadFile, File, HTTPException from fastapi.responses import JSONResponse import pandas as pd from datetime import datetime import logging from io import BytesIO from app.core.database import execute_query, execute_update logger = logging.getLogger(__name__) router = APIRouter() def parse_danish_number(value): """Convert Danish number format to float (2.995,00 -> 2995.00)""" if pd.isna(value) or value == '': return 0.0 if isinstance(value, (int, float)): return float(value) value_str = str(value).strip() value_str = value_str.replace('.', '').replace(',', '.') try: return float(value_str) except: return 0.0 def parse_danish_date(value): """Convert DD.MM.YYYY to YYYY-MM-DD""" if pd.isna(value) or value == '': return None if isinstance(value, datetime): return value.strftime('%Y-%m-%d') value_str = str(value).strip() try: dt = datetime.strptime(value_str, '%d.%m.%Y') return dt.strftime('%Y-%m-%d') except: try: dt = pd.to_datetime(value_str) return dt.strftime('%Y-%m-%d') except: return None @router.post("/admin/bmc-office-subscriptions/upload") async def upload_bmc_office_subscriptions(file: UploadFile = File(...)): """ Upload Excel file with BMC Office subscriptions Expected columns: FirmaID, Firma, Startdate, Text, Antal, Pris, Rabat, Beskrivelse, FakturaFirmaID, FakturaFirma """ if not file.filename.endswith(('.xlsx', '.xls')): raise HTTPException(status_code=400, detail="Kun Excel filer (.xlsx, .xls) er tilladt") try: # Read Excel file contents = await file.read() df = pd.read_excel(BytesIO(contents)) logger.info(f"📂 Læst Excel fil: {file.filename} med {len(df)} rækker") logger.info(f"📋 Kolonner: {', '.join(df.columns)}") # Get all customers from database customers = execute_query("SELECT id, name FROM customers ORDER BY name") customer_map = {c['name'].lower().strip(): c['id'] for c in customers} logger.info(f"✅ Fundet {len(customers)} kunder i databasen") # Clear existing subscriptions execute_update("DELETE FROM bmc_office_subscriptions", ()) logger.info("🗑️ Ryddet eksisterende abonnementer") # Process rows imported = 0 skipped = 0 errors = [] for idx, row in df.iterrows(): try: firma_name = str(row.get('Firma', '')).strip() if not firma_name: skipped += 1 continue # Find customer by name (case-insensitive) customer_id = None firma_lower = firma_name.lower() # Exact match first if firma_lower in customer_map: customer_id = customer_map[firma_lower] else: # Partial match for cust_name, cust_id in customer_map.items(): if firma_lower in cust_name or cust_name in firma_lower: customer_id = cust_id break if not customer_id: skipped += 1 errors.append(f"Række {idx+2}: Kunde '{firma_name}' ikke fundet") continue # Parse data firma_id = str(row.get('FirmaID', '')).strip() start_date = parse_danish_date(row.get('Startdate')) text = str(row.get('Text', '')).strip() antal = parse_danish_number(row.get('Antal', 1)) pris = parse_danish_number(row.get('Pris', 0)) rabat = parse_danish_number(row.get('Rabat', 0)) beskrivelse = str(row.get('Beskrivelse', '')).strip() faktura_firma_id = str(row.get('FakturaFirmaID', '')).strip() faktura_firma_name = str(row.get('FakturaFirma', '')).strip() # Insert subscription execute_update( """INSERT INTO bmc_office_subscriptions (customer_id, firma_id, firma_name, start_date, text, antal, pris, rabat, beskrivelse, faktura_firma_id, faktura_firma_name, active) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", (customer_id, firma_id, firma_name, start_date, text, antal, pris, rabat, beskrivelse, faktura_firma_id, faktura_firma_name, True) ) imported += 1 except Exception as e: skipped += 1 errors.append(f"Række {idx+2}: {str(e)}") logger.error(f"❌ Fejl ved import af række {idx+2}: {e}") # Get statistics stats = execute_query(""" SELECT COUNT(*) as total_records, COUNT(CASE WHEN active THEN 1 END) as active_records, ROUND(SUM(CASE WHEN active THEN total_inkl_moms ELSE 0 END)::numeric, 2) as total_value_dkk FROM bmc_office_subscription_totals """)[0] logger.info(f"✅ Import færdig: {imported} importeret, {skipped} sprunget over") return { "success": True, "filename": file.filename, "total_rows": len(df), "imported": imported, "skipped": skipped, "errors": errors[:10], # Max 10 error messages "statistics": { "total_records": stats['total_records'], "active_records": stats['active_records'], "total_value_dkk": float(stats['total_value_dkk'] or 0) } } except Exception as e: logger.error(f"❌ Fejl ved upload: {e}") raise HTTPException(status_code=500, detail=f"Fejl ved upload: {str(e)}")