Fix: Bedre case title fallback og klarere fejlbesked for manglende customer linking
This commit is contained in:
parent
e69f211fbf
commit
68eb1d31d1
@ -248,10 +248,21 @@ class EconomicExportService:
|
|||||||
customer_data = execute_query_single(customer_number_query, (order['customer_id'],))
|
customer_data = execute_query_single(customer_number_query, (order['customer_id'],))
|
||||||
|
|
||||||
if not customer_data or not customer_data.get('economic_customer_number'):
|
if not customer_data or not customer_data.get('economic_customer_number'):
|
||||||
raise HTTPException(
|
# Check if customer is linked at all
|
||||||
status_code=400,
|
check_link = execute_query_single(
|
||||||
detail=f"Customer {order['customer_name']} has no e-conomic customer number. Link customer to Hub customer first."
|
"SELECT hub_customer_id FROM tmodule_customers WHERE id = %s",
|
||||||
|
(order['customer_id'],)
|
||||||
)
|
)
|
||||||
|
if not check_link or not check_link.get('hub_customer_id'):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail=f"Customer '{order['customer_name']}' er ikke linket til en Hub kunde. Gå til vTiger kunder og link kunden først."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail=f"Customer '{order['customer_name']}' mangler e-conomic kundenummer. Opdater kunde i Customers modulet."
|
||||||
|
)
|
||||||
|
|
||||||
customer_number = customer_data['economic_customer_number']
|
customer_number = customer_data['economic_customer_number']
|
||||||
|
|
||||||
|
|||||||
@ -125,9 +125,11 @@ class OrderService:
|
|||||||
for time_entry in approved_times:
|
for time_entry in approved_times:
|
||||||
case_id = time_entry['case_id']
|
case_id = time_entry['case_id']
|
||||||
if case_id not in case_groups:
|
if case_id not in case_groups:
|
||||||
|
# Prioriter case title fra vTiger (c.title), fallback til vtiger_data title
|
||||||
|
case_title = time_entry.get('case_title') or time_entry.get('vtiger_title')
|
||||||
case_groups[case_id] = {
|
case_groups[case_id] = {
|
||||||
'case_vtiger_id': time_entry.get('case_vtiger_id'),
|
'case_vtiger_id': time_entry.get('case_vtiger_id'),
|
||||||
'case_title': time_entry.get('case_title'), # Case titel fra vTiger
|
'case_title': case_title, # Case titel fra vTiger
|
||||||
'contact_name': time_entry.get('contact_name'),
|
'contact_name': time_entry.get('contact_name'),
|
||||||
'worked_date': time_entry.get('worked_date'), # Seneste dato
|
'worked_date': time_entry.get('worked_date'), # Seneste dato
|
||||||
'is_travel': False, # Marker hvis nogen entry er rejse
|
'is_travel': False, # Marker hvis nogen entry er rejse
|
||||||
|
|||||||
155
compare_schemas.py
Executable file
155
compare_schemas.py
Executable file
@ -0,0 +1,155 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Compare local dev database schema with production to find missing columns
|
||||||
|
"""
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
# Get schema from local dev database
|
||||||
|
def get_local_schema():
|
||||||
|
"""Get all tables and columns from local dev database"""
|
||||||
|
print("🔍 Fetching LOCAL dev schema...")
|
||||||
|
result = subprocess.run(
|
||||||
|
["docker", "exec", "bmc_hub_dev-postgres-1",
|
||||||
|
"psql", "-U", "bmcnetworks", "-d", "bmc_hub", "-t", "-c",
|
||||||
|
"""
|
||||||
|
SELECT table_name, column_name, data_type, character_maximum_length, is_nullable, column_default
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_schema = 'public'
|
||||||
|
ORDER BY table_name, ordinal_position;
|
||||||
|
"""],
|
||||||
|
capture_output=True,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
|
||||||
|
schema = defaultdict(list)
|
||||||
|
for line in result.stdout.strip().split('\n'):
|
||||||
|
if not line.strip():
|
||||||
|
continue
|
||||||
|
parts = [p.strip() for p in line.split('|')]
|
||||||
|
if len(parts) >= 2:
|
||||||
|
table = parts[0]
|
||||||
|
column = parts[1]
|
||||||
|
data_type = parts[2] if len(parts) > 2 else ''
|
||||||
|
schema[table].append({
|
||||||
|
'column': column,
|
||||||
|
'type': data_type,
|
||||||
|
'full': line
|
||||||
|
})
|
||||||
|
|
||||||
|
return schema
|
||||||
|
|
||||||
|
# Get schema from production via SSH
|
||||||
|
def get_prod_schema():
|
||||||
|
"""Get all tables and columns from production database"""
|
||||||
|
print("🔍 Fetching PRODUCTION schema...")
|
||||||
|
result = subprocess.run(
|
||||||
|
["ssh", "bmcadmin@172.16.31.183",
|
||||||
|
"sudo podman exec bmc-hub-postgres-prod psql -U bmc_hub -d bmc_hub -t -c \"SELECT table_name, column_name, data_type, character_maximum_length, is_nullable, column_default FROM information_schema.columns WHERE table_schema = 'public' ORDER BY table_name, ordinal_position;\""],
|
||||||
|
capture_output=True,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
|
||||||
|
schema = defaultdict(list)
|
||||||
|
for line in result.stdout.strip().split('\n'):
|
||||||
|
if not line.strip():
|
||||||
|
continue
|
||||||
|
parts = [p.strip() for p in line.split('|')]
|
||||||
|
if len(parts) >= 2:
|
||||||
|
table = parts[0]
|
||||||
|
column = parts[1]
|
||||||
|
data_type = parts[2] if len(parts) > 2 else ''
|
||||||
|
schema[table].append({
|
||||||
|
'column': column,
|
||||||
|
'type': data_type,
|
||||||
|
'full': line
|
||||||
|
})
|
||||||
|
|
||||||
|
return schema
|
||||||
|
|
||||||
|
def compare_schemas(local, prod):
|
||||||
|
"""Compare schemas and find missing columns"""
|
||||||
|
print("\n📊 Comparing schemas...\n")
|
||||||
|
|
||||||
|
missing = defaultdict(list)
|
||||||
|
|
||||||
|
# Check each table in local
|
||||||
|
for table in sorted(local.keys()):
|
||||||
|
local_cols = {col['column']: col for col in local[table]}
|
||||||
|
prod_cols = {col['column']: col for col in prod.get(table, [])}
|
||||||
|
|
||||||
|
# Find missing columns
|
||||||
|
for col_name, col_info in local_cols.items():
|
||||||
|
if col_name not in prod_cols:
|
||||||
|
missing[table].append(col_info)
|
||||||
|
|
||||||
|
return missing
|
||||||
|
|
||||||
|
def print_missing(missing):
|
||||||
|
"""Print missing columns in a readable format"""
|
||||||
|
if not missing:
|
||||||
|
print("✅ No missing columns! Schemas are in sync.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("❌ MISSING COLUMNS IN PRODUCTION:\n")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
for table in sorted(missing.keys()):
|
||||||
|
print(f"\n📋 Table: {table}")
|
||||||
|
print("-" * 80)
|
||||||
|
for col in missing[table]:
|
||||||
|
print(f" ❌ {col['column']} ({col['type']})")
|
||||||
|
|
||||||
|
print("\n" + "=" * 80)
|
||||||
|
print(f"\n📊 Summary: {sum(len(cols) for cols in missing.values())} missing columns across {len(missing)} tables")
|
||||||
|
|
||||||
|
def generate_sql(missing):
|
||||||
|
"""Generate SQL to add missing columns"""
|
||||||
|
if not missing:
|
||||||
|
return
|
||||||
|
|
||||||
|
print("\n\n🔧 SQL TO ADD MISSING COLUMNS:")
|
||||||
|
print("=" * 80)
|
||||||
|
print()
|
||||||
|
|
||||||
|
for table in sorted(missing.keys()):
|
||||||
|
print(f"-- Table: {table}")
|
||||||
|
for col in missing[table]:
|
||||||
|
col_type = col['type']
|
||||||
|
# Simplified type mapping - you may need to adjust
|
||||||
|
if 'character varying' in col_type:
|
||||||
|
col_type = 'VARCHAR(255)'
|
||||||
|
elif col_type == 'integer':
|
||||||
|
col_type = 'INTEGER'
|
||||||
|
elif 'numeric' in col_type:
|
||||||
|
col_type = 'NUMERIC(10,2)'
|
||||||
|
elif col_type == 'boolean':
|
||||||
|
col_type = 'BOOLEAN DEFAULT false'
|
||||||
|
elif col_type == 'text':
|
||||||
|
col_type = 'TEXT'
|
||||||
|
elif 'timestamp' in col_type:
|
||||||
|
col_type = 'TIMESTAMP'
|
||||||
|
elif col_type == 'date':
|
||||||
|
col_type = 'DATE'
|
||||||
|
elif col_type == 'jsonb':
|
||||||
|
col_type = 'JSONB'
|
||||||
|
|
||||||
|
print(f"ALTER TABLE {table} ADD COLUMN IF NOT EXISTS {col['column']} {col_type};")
|
||||||
|
print()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
local = get_local_schema()
|
||||||
|
prod = get_prod_schema()
|
||||||
|
|
||||||
|
missing = compare_schemas(local, prod)
|
||||||
|
|
||||||
|
print_missing(missing)
|
||||||
|
generate_sql(missing)
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"❌ Error: {e}")
|
||||||
|
print(f"Output: {e.output}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error: {e}")
|
||||||
Loading…
Reference in New Issue
Block a user