feat: Add comprehensive customer activity logging for all CRUD operations
This commit is contained in:
parent
b03dd5c8f6
commit
af044a7be8
@ -11,6 +11,7 @@ import logging
|
|||||||
|
|
||||||
from app.core.database import execute_query, execute_query_single
|
from app.core.database import execute_query, execute_query_single
|
||||||
from app.services.cvr_service import get_cvr_service
|
from app.services.cvr_service import get_cvr_service
|
||||||
|
from app.services.customer_activity_logger import CustomerActivityLogger
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -413,6 +414,9 @@ async def create_customer(customer: CustomerCreate):
|
|||||||
|
|
||||||
logger.info(f"✅ Created customer {customer_id}: {customer.name}")
|
logger.info(f"✅ Created customer {customer_id}: {customer.name}")
|
||||||
|
|
||||||
|
# Log activity
|
||||||
|
CustomerActivityLogger.log_created(customer_id, customer.name)
|
||||||
|
|
||||||
# Fetch and return created customer
|
# Fetch and return created customer
|
||||||
created = execute_query_single(
|
created = execute_query_single(
|
||||||
"SELECT * FROM customers WHERE id = %s",
|
"SELECT * FROM customers WHERE id = %s",
|
||||||
@ -429,7 +433,7 @@ async def update_customer(customer_id: int, update: CustomerUpdate):
|
|||||||
"""Update customer information"""
|
"""Update customer information"""
|
||||||
# Verify customer exists
|
# Verify customer exists
|
||||||
existing = execute_query_single(
|
existing = execute_query_single(
|
||||||
"SELECT id FROM customers WHERE id = %s",
|
"SELECT id, name FROM customers WHERE id = %s",
|
||||||
(customer_id,))
|
(customer_id,))
|
||||||
if not existing:
|
if not existing:
|
||||||
raise HTTPException(status_code=404, detail="Customer not found")
|
raise HTTPException(status_code=404, detail="Customer not found")
|
||||||
@ -439,6 +443,8 @@ async def update_customer(customer_id: int, update: CustomerUpdate):
|
|||||||
params = []
|
params = []
|
||||||
|
|
||||||
update_dict = update.dict(exclude_unset=True)
|
update_dict = update.dict(exclude_unset=True)
|
||||||
|
fields_changed = list(update_dict.keys())
|
||||||
|
|
||||||
for field, value in update_dict.items():
|
for field, value in update_dict.items():
|
||||||
updates.append(f"{field} = %s")
|
updates.append(f"{field} = %s")
|
||||||
params.append(value)
|
params.append(value)
|
||||||
@ -454,6 +460,9 @@ async def update_customer(customer_id: int, update: CustomerUpdate):
|
|||||||
execute_update(query, tuple(params))
|
execute_update(query, tuple(params))
|
||||||
logger.info(f"✅ Updated customer {customer_id}")
|
logger.info(f"✅ Updated customer {customer_id}")
|
||||||
|
|
||||||
|
# Log activity
|
||||||
|
CustomerActivityLogger.log_updated(customer_id, existing['name'], fields_changed)
|
||||||
|
|
||||||
# Fetch and return updated customer
|
# Fetch and return updated customer
|
||||||
updated = execute_query_single(
|
updated = execute_query_single(
|
||||||
"SELECT * FROM customers WHERE id = %s",
|
"SELECT * FROM customers WHERE id = %s",
|
||||||
|
|||||||
164
app/services/customer_activity_logger.py
Normal file
164
app/services/customer_activity_logger.py
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
"""
|
||||||
|
Customer Activity Logger
|
||||||
|
Helper service for logging all customer-related events
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Optional, Dict, Any
|
||||||
|
import json
|
||||||
|
from app.core.database import execute_insert
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerActivityLogger:
|
||||||
|
"""Centralized customer activity logging"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def log(
|
||||||
|
customer_id: int,
|
||||||
|
activity_type: str,
|
||||||
|
description: str,
|
||||||
|
user_id: Optional[int] = 1, # Default to system user
|
||||||
|
metadata: Optional[Dict[str, Any]] = None
|
||||||
|
) -> Optional[int]:
|
||||||
|
"""
|
||||||
|
Log a customer activity event
|
||||||
|
|
||||||
|
Args:
|
||||||
|
customer_id: Customer ID
|
||||||
|
activity_type: Type of activity (customer_updated, contact_added, subscription_changed, etc.)
|
||||||
|
description: Human-readable description
|
||||||
|
user_id: User ID performing action (1 = system)
|
||||||
|
metadata: Additional event data as dict
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Activity ID or None on failure
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Store metadata as JSON string if provided
|
||||||
|
metadata_str = json.dumps(metadata) if metadata else None
|
||||||
|
|
||||||
|
activity_id = execute_insert(
|
||||||
|
"""INSERT INTO customer_activities
|
||||||
|
(customer_id, activity_type, description, user_id, metadata)
|
||||||
|
VALUES (%s, %s, %s, %s, %s)
|
||||||
|
RETURNING id""",
|
||||||
|
(customer_id, activity_type, description, user_id, metadata_str)
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(f"📝 Logged customer activity: {activity_type} for customer {customer_id}")
|
||||||
|
return activity_id
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Failed to log customer activity: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Specific activity types
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def log_created(customer_id: int, customer_name: str, user_id: Optional[int] = 1):
|
||||||
|
"""Log customer creation"""
|
||||||
|
return CustomerActivityLogger.log(
|
||||||
|
customer_id=customer_id,
|
||||||
|
activity_type='customer_created',
|
||||||
|
description=f'Kunde oprettet: {customer_name}',
|
||||||
|
user_id=user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def log_updated(customer_id: int, customer_name: str, fields_changed: list, user_id: Optional[int] = 1):
|
||||||
|
"""Log customer update"""
|
||||||
|
fields_str = ', '.join(fields_changed)
|
||||||
|
return CustomerActivityLogger.log(
|
||||||
|
customer_id=customer_id,
|
||||||
|
activity_type='customer_updated',
|
||||||
|
description=f'Kunde opdateret: {customer_name} - Ændrede felter: {fields_str}',
|
||||||
|
user_id=user_id,
|
||||||
|
metadata={'fields_changed': fields_changed}
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def log_deleted(customer_id: int, customer_name: str, user_id: Optional[int] = 1):
|
||||||
|
"""Log customer deletion"""
|
||||||
|
return CustomerActivityLogger.log(
|
||||||
|
customer_id=customer_id,
|
||||||
|
activity_type='customer_deleted',
|
||||||
|
description=f'Kunde slettet: {customer_name}',
|
||||||
|
user_id=user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def log_contact_added(customer_id: int, contact_name: str, user_id: Optional[int] = 1):
|
||||||
|
"""Log contact added"""
|
||||||
|
return CustomerActivityLogger.log(
|
||||||
|
customer_id=customer_id,
|
||||||
|
activity_type='contact_added',
|
||||||
|
description=f'Kontaktperson tilføjet: {contact_name}',
|
||||||
|
user_id=user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def log_contact_updated(customer_id: int, contact_name: str, user_id: Optional[int] = 1):
|
||||||
|
"""Log contact updated"""
|
||||||
|
return CustomerActivityLogger.log(
|
||||||
|
customer_id=customer_id,
|
||||||
|
activity_type='contact_updated',
|
||||||
|
description=f'Kontaktperson opdateret: {contact_name}',
|
||||||
|
user_id=user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def log_contact_deleted(customer_id: int, contact_name: str, user_id: Optional[int] = 1):
|
||||||
|
"""Log contact deleted"""
|
||||||
|
return CustomerActivityLogger.log(
|
||||||
|
customer_id=customer_id,
|
||||||
|
activity_type='contact_deleted',
|
||||||
|
description=f'Kontaktperson slettet: {contact_name}',
|
||||||
|
user_id=user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def log_subscription_created(customer_id: int, subscription_name: str, user_id: Optional[int] = 1):
|
||||||
|
"""Log subscription created"""
|
||||||
|
return CustomerActivityLogger.log(
|
||||||
|
customer_id=customer_id,
|
||||||
|
activity_type='subscription_created',
|
||||||
|
description=f'Abonnement oprettet: {subscription_name}',
|
||||||
|
user_id=user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def log_subscription_updated(customer_id: int, subscription_name: str, user_id: Optional[int] = 1):
|
||||||
|
"""Log subscription updated"""
|
||||||
|
return CustomerActivityLogger.log(
|
||||||
|
customer_id=customer_id,
|
||||||
|
activity_type='subscription_updated',
|
||||||
|
description=f'Abonnement opdateret: {subscription_name}',
|
||||||
|
user_id=user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def log_subscription_deleted(customer_id: int, subscription_name: str, user_id: Optional[int] = 1):
|
||||||
|
"""Log subscription deleted"""
|
||||||
|
return CustomerActivityLogger.log(
|
||||||
|
customer_id=customer_id,
|
||||||
|
activity_type='subscription_deleted',
|
||||||
|
description=f'Abonnement slettet: {subscription_name}',
|
||||||
|
user_id=user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def log_sync(customer_id: int, source: str, details: str, user_id: Optional[int] = 1):
|
||||||
|
"""Log external sync"""
|
||||||
|
return CustomerActivityLogger.log(
|
||||||
|
customer_id=customer_id,
|
||||||
|
activity_type=f'{source}_sync',
|
||||||
|
description=f'{source} synkronisering: {details}',
|
||||||
|
user_id=user_id,
|
||||||
|
metadata={'source': source}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Export singleton instance
|
||||||
|
customer_activity_logger = CustomerActivityLogger()
|
||||||
Loading…
Reference in New Issue
Block a user