You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

314 lines
11 KiB

"""
Splynx Repository Module
This module provides a repository wrapper around the Splynx API client.
It adds additional error handling, retry logic, and commonly used operations.
"""
import logging
import time
from typing import Dict, Any, Optional, List
logger = logging.getLogger(__name__)
class SplynxRepository:
"""
Repository for Splynx API operations.
This class wraps the Splynx API client and provides higher-level
operations with consistent error handling and retry logic.
"""
def __init__(self, splynx_client, max_retries: int = 3, retry_delay: float = 1.0):
"""
Initialize the Splynx repository.
Args:
splynx_client: Splynx API client instance
max_retries: Maximum number of retry attempts for transient failures
retry_delay: Delay in seconds between retries
"""
self.splynx = splynx_client
self.max_retries = max_retries
self.retry_delay = retry_delay
def _retry_operation(self, operation_func, operation_name: str):
"""
Execute an operation with retry logic for transient failures.
Args:
operation_func: Function to execute
operation_name: Description for logging
Returns:
Result of operation_func or None if all retries failed
"""
last_error = None
for attempt in range(self.max_retries):
try:
return operation_func()
except Exception as e:
last_error = e
if attempt < self.max_retries - 1:
logger.warning(f"{operation_name} failed (attempt {attempt + 1}/{self.max_retries}): {e}")
time.sleep(self.retry_delay)
else:
logger.error(f"{operation_name} failed after {self.max_retries} attempts: {e}")
return None
def get_customer(self, customer_id: int) -> Optional[Dict[str, Any]]:
"""
Get customer information from Splynx.
Args:
customer_id: Customer ID in Splynx
Returns:
Customer data dictionary or None if not found
"""
def _get():
customer_data = self.splynx.Customer(customer_id)
return customer_data if customer_data != 'unknown' else None
return self._retry_operation(_get, f"Get customer {customer_id}")
def get_customer_name(self, customer_id: int) -> str:
"""
Get customer name from Splynx.
Args:
customer_id: Customer ID in Splynx
Returns:
Customer name or "Unknown Customer" if not found
"""
customer_data = self.get_customer(customer_id)
if customer_data:
return customer_data.get('name', 'Unknown Customer')
return 'Unknown Customer'
def mark_invoices_paid(self, customer_id: int) -> List[Dict[str, Any]]:
"""
Mark all unpaid/pending invoices as paid for a customer.
Args:
customer_id: Customer ID in Splynx
Returns:
List of updated invoice dictionaries
"""
def _mark():
result = self.splynx.get(
url=f"/api/2.0/admin/finance/invoices?main_attributes[customer_id]={customer_id}&main_attributes[status]=not_paid&main_attributes[status]=pending"
)
invoice_pay = {"status": "paid"}
updated_invoices = []
for invoice in result:
res = self.splynx.put(url=f"/api/2.0/admin/finance/invoices/{invoice['id']}", params=invoice_pay)
if res:
updated_invoices.append(res)
logger.info(f"Marked {len(updated_invoices)} invoices as paid for customer {customer_id}")
return updated_invoices
return self._retry_operation(_mark, f"Mark invoices paid for customer {customer_id}") or []
def mark_invoices_pending(self, customer_id: int) -> List[Dict[str, Any]]:
"""
Mark all unpaid invoices as pending for a customer.
Args:
customer_id: Customer ID in Splynx
Returns:
List of updated invoice dictionaries
"""
def _mark():
result = self.splynx.get(
url=f"/api/2.0/admin/finance/invoices?main_attributes[customer_id]={customer_id}&main_attributes[status]=not_paid"
)
invoice_pay = {"status": "pending"}
updated_invoices = []
for invoice in result:
res = self.splynx.put(url=f"/api/2.0/admin/finance/invoices/{invoice['id']}", params=invoice_pay)
if res:
updated_invoices.append(res)
logger.info(f"Marked {len(updated_invoices)} invoices as pending for customer {customer_id}")
return updated_invoices
return self._retry_operation(_mark, f"Mark invoices pending for customer {customer_id}") or []
def revert_invoices_to_unpaid(self, customer_id: int) -> List[Dict[str, Any]]:
"""
Revert pending invoices back to unpaid status for a customer.
Args:
customer_id: Customer ID in Splynx
Returns:
List of updated invoice dictionaries
"""
def _revert():
result = self.splynx.get(
url=f"/api/2.0/admin/finance/invoices?main_attributes[customer_id]={customer_id}&main_attributes[status]=pending"
)
invoice_pay = {"status": "not_paid"}
updated_invoices = []
for invoice in result:
res = self.splynx.put(url=f"/api/2.0/admin/finance/invoices/{invoice['id']}", params=invoice_pay)
if res:
updated_invoices.append(res)
logger.info(f"Reverted {len(updated_invoices)} invoices to unpaid for customer {customer_id}")
return updated_invoices
return self._retry_operation(_revert, f"Revert invoices to unpaid for customer {customer_id}") or []
def add_payment(self, customer_id: int, amount: float, payment_intent_id: str, payment_id: int) -> Optional[int]:
"""
Add a payment record to Splynx.
Args:
customer_id: Customer ID in Splynx
amount: Payment amount
payment_intent_id: Stripe payment intent ID
payment_id: Internal payment ID
Returns:
Splynx payment ID if successful, None otherwise
"""
def _add():
from datetime import datetime
stripe_pay = {
"customer_id": customer_id,
"amount": amount,
"date": str(datetime.now().strftime('%Y-%m-%d')),
"field_1": payment_intent_id,
"field_2": f"Payment_ID (Batch): {payment_id}"
}
res = self.splynx.post(url="/api/2.0/admin/finance/payments", params=stripe_pay)
if res:
logger.info(f"Added payment to Splynx for customer {customer_id}: payment ID {res['id']}")
return res['id']
else:
logger.error(f"Failed to add payment to Splynx for customer {customer_id}")
return None
return self._retry_operation(_add, f"Add payment to Splynx for customer {customer_id}")
def delete_payment(self, customer_id: int, payment_intent_id: str) -> Dict[str, Any]:
"""
Delete a Splynx payment record by customer ID and payment intent.
Args:
customer_id: Customer ID in Splynx
payment_intent_id: Stripe payment intent ID
Returns:
Dictionary with success status and details
"""
try:
params = {
'main_attributes': {
'customer_id': customer_id,
'field_1': payment_intent_id
},
}
query_string = self.splynx.build_splynx_query_params(params)
result = self.splynx.get(url=f"/api/2.0/admin/finance/payments?{query_string}")
if not result:
logger.warning(f"No Splynx payment found for customer {customer_id}, payment intent {payment_intent_id}")
return {'success': False, 'error': 'No payment found to delete'}
logger.info(f"Found {len(result)} Splynx payment(s) to delete for customer {customer_id}")
delete_success = self.splynx.delete(url=f"/api/2.0/admin/finance/payments/{result[0]['id']}")
if delete_success:
logger.info(f"Successfully deleted Splynx Payment ID: {result[0]['id']} for customer: {customer_id}")
return {
'success': True,
'deleted_payment_id': result[0]['id'],
'customer_id': customer_id,
'payment_intent': payment_intent_id
}
else:
logger.error(f"Failed to delete Splynx Payment ID: {result[0]['id']} for customer: {customer_id}")
return {'success': False, 'error': 'Delete operation failed'}
except Exception as e:
logger.error(f"Error deleting Splynx payment for customer {customer_id}: {e}")
return {'success': False, 'error': str(e)}
def create_ticket(self, customer_id: int, subject: str, priority: str = "medium",
type_id: int = 1, group_id: int = 7, status_id: int = 1) -> Dict[str, Any]:
"""
Create a support ticket in Splynx.
Args:
customer_id: Customer ID in Splynx
subject: Ticket subject
priority: Ticket priority (low, medium, high)
type_id: Ticket type ID
group_id: Ticket group ID
status_id: Ticket status ID
Returns:
Dictionary with success status and ticket ID
"""
def _create():
return self.splynx.create_ticket(
customer_id=customer_id,
subject=subject,
priority=priority,
type_id=type_id,
group_id=group_id,
status_id=status_id
)
result = self._retry_operation(_create, f"Create ticket for customer {customer_id}")
if result and result.get('success'):
logger.info(f"Created ticket #{result['ticket_id']} for customer {customer_id}")
return result or {'success': False, 'error': 'Failed to create ticket'}
def add_ticket_message(self, ticket_id: int, message: str, is_admin: bool = False,
hide_for_customer: bool = False, message_type: str = "message") -> bool:
"""
Add a message to an existing ticket.
Args:
ticket_id: Ticket ID
message: Message content (HTML)
is_admin: Whether message is from admin
hide_for_customer: Whether to hide message from customer
message_type: Message type ("message" or "note")
Returns:
True if successful, False otherwise
"""
def _add():
return self.splynx.add_ticket_message(
ticket_id=ticket_id,
message=message,
is_admin=is_admin,
hide_for_customer=hide_for_customer,
message_type=message_type
)
result = self._retry_operation(_add, f"Add message to ticket {ticket_id}")
if result:
logger.info(f"Added {message_type} to ticket #{ticket_id}")
return True
return False