bmc_hub/scripts/import_bmc_office_subscriptions.py
Christian 38fa3b6c0a feat: Add subscriptions lock feature to customers
- Added a new column `subscriptions_locked` to the `customers` table to manage subscription access.
- Implemented a script to create new modules from a template, including updates to various files (module.json, README.md, router.py, views.py, and migration SQL).
- Developed a script to import BMC Office subscriptions from an Excel file into the database, including error handling and statistics reporting.
- Created a script to lookup and update missing CVR numbers using the CVR.dk API.
- Implemented a script to relink Hub customers to e-conomic customer numbers based on name matching.
- Developed scripts to sync CVR numbers from Simply-CRM and vTiger to the local customers database.
2025-12-13 12:06:28 +01:00

221 lines
7.5 KiB
Python

#!/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)