#!/usr/bin/env python3 """ Import BMC Office abonnementer fra Excel fil til database """ import sys import os import pandas as pd from datetime import datetime from dotenv import load_dotenv # Load environment variables load_dotenv() # Override DATABASE_URL for local execution (postgres:5432 -> localhost:5433) if "postgres:5432" in os.getenv("DATABASE_URL", ""): os.environ["DATABASE_URL"] = os.getenv("DATABASE_URL").replace("postgres:5432", "localhost:5433") # Add parent directory to path to import app modules sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) from app.core.database import execute_query, execute_update, get_db_connection, init_db 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) # Convert string: remove dots (thousands separator) and replace comma with dot 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: # Try DD.MM.YYYY format dt = datetime.strptime(value_str, '%d.%m.%Y') return dt.strftime('%Y-%m-%d') except: try: # Try other common formats dt = pd.to_datetime(value_str) return dt.strftime('%Y-%m-%d') except: return None def find_customer_by_name(name, all_customers): """Find customer in database by name (case-insensitive, fuzzy match)""" if not name: return None name_lower = name.lower().strip() # First try exact match for customer in all_customers: if customer['name'].lower().strip() == name_lower: return customer['id'] # Then try partial match for customer in all_customers: customer_name = customer['name'].lower().strip() if name_lower in customer_name or customer_name in name_lower: return customer['id'] return None def import_subscriptions(excel_file): """Import subscriptions from Excel file""" print(f"📂 Læser Excel fil: {excel_file}") # Read Excel file df = pd.read_excel(excel_file) print(f"✅ Fundet {len(df)} rækker i Excel filen") print(f"📋 Kolonner: {', '.join(df.columns)}") # Get all customers from database customers = execute_query("SELECT id, name, vtiger_id FROM customers ORDER BY name") print(f"✅ Fundet {len(customers)} kunder i databasen") # Clear existing BMC Office subscriptions print("\n🗑️ Rydder eksisterende BMC Office abonnementer...") execute_update("DELETE FROM bmc_office_subscriptions", ()) # Process each row imported = 0 skipped = 0 errors = [] print("\n📥 Importerer abonnementer...") for idx, row in df.iterrows(): try: # Extract data firma_id = str(row.get('FirmaID', '')) firma_name = str(row.get('Firma', '')) start_date = parse_danish_date(row.get('Startdate')) text = str(row.get('Text', '')) 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', '')) faktura_firma_id = str(row.get('FakturaFirmaID', '')) faktura_firma_name = str(row.get('Fakturafirma', '')) # Find customer by faktura firma name (most reliable) customer_id = find_customer_by_name(faktura_firma_name, customers) # If not found, try firma name if not customer_id: customer_id = find_customer_by_name(firma_name, customers) if not customer_id: skipped += 1 errors.append(f"Række {idx+2}: Kunde ikke fundet: {faktura_firma_name} / {firma_name}") continue # Determine if active (rabat < 100%) active = (pris - rabat) > 0 # Insert into database query = """ 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) """ execute_update(query, ( customer_id, firma_id, firma_name, start_date, text, antal, pris, rabat, beskrivelse if beskrivelse != 'nan' else '', faktura_firma_id, faktura_firma_name, active )) imported += 1 if imported % 50 == 0: print(f" ✓ Importeret {imported} abonnementer...") except Exception as e: skipped += 1 errors.append(f"Række {idx+2}: {str(e)}") # Summary print(f"\n{'='*60}") print(f"✅ Import færdig!") print(f" Importeret: {imported}") print(f" Sprunget over: {skipped}") print(f"{'='*60}") if errors: print(f"\n⚠️ Fejl og advarsler:") for error in errors[:20]: # Show first 20 errors print(f" {error}") if len(errors) > 20: print(f" ... og {len(errors)-20} flere") # Show statistics stats_query = """ SELECT COUNT(*) as total, COUNT(*) FILTER (WHERE active = true) as active, SUM((antal * pris - rabat) * 1.25) FILTER (WHERE active = true) as total_value FROM bmc_office_subscriptions """ stats = execute_query(stats_query, fetchone=True) print(f"\n📊 Statistik:") print(f" Total abonnementer: {stats['total']}") print(f" Aktive abonnementer: {stats['active']}") print(f" Total værdi (aktive): {stats['total_value']:.2f} DKK inkl. moms") # Show top customers top_query = """ SELECT c.name, COUNT(*) as antal_abonnementer, SUM((bos.antal * bos.pris - bos.rabat) * 1.25) as total_value FROM bmc_office_subscriptions bos JOIN customers c ON c.id = bos.customer_id WHERE bos.active = true GROUP BY c.id, c.name ORDER BY total_value DESC LIMIT 10 """ top_customers = execute_query(top_query) if top_customers: print(f"\n🏆 Top 10 kunder (efter værdi):") for i, cust in enumerate(top_customers, 1): print(f" {i}. {cust['name']}: {cust['antal_abonnementer']} abonnementer = {cust['total_value']:.2f} DKK") if __name__ == '__main__': excel_file = '/Users/christianthomas/DEV/bmc_hub_dev/uploads/BMC_FasteOmkostninger_20251211.xlsx' if not os.path.exists(excel_file): print(f"❌ Fil ikke fundet: {excel_file}") sys.exit(1) try: # Initialize database connection pool print("🔌 Forbinder til database...") init_db() import_subscriptions(excel_file) print("\n✅ Import succesfuld!") except Exception as e: print(f"\n❌ Import fejlede: {e}") import traceback traceback.print_exc() sys.exit(1)