Files
desk-moloni/modules/desk_moloni/models/Desk_moloni_sync_log_model.php
Emanuel Almeida c19f6fd9ee fix(perfexcrm module): align version to 3.0.1, unify entrypoint, and harden routes/views
- Bump DESK_MOLONI version to 3.0.1 across module
- Normalize hooks to after_client_* and instantiate PerfexHooks safely
- Fix OAuthController view path and API client class name
- Add missing admin views for webhook config/logs; adjust view loading
- Harden client portal routes and admin routes mapping
- Make Dashboard/Logs/Queue tolerant to optional model methods
- Align log details query with existing schema; avoid broken joins

This makes the module operational in Perfex (admin + client), reduces 404s,
and avoids fatal errors due to inconsistent tables/methods.
2025-09-11 17:38:45 +01:00

1000 lines
34 KiB
PHP

<?php
/**
* Desk_moloni_sync_log_model.php
*
* Model for desk_moloni_sync_log table
* Handles comprehensive audit log of all synchronization operations
*
* @package DeskMoloni\Models
* @author Database Design Specialist
* @version 3.0
*/
defined('BASEPATH') or exit('No direct script access allowed');
require_once(dirname(__FILE__) . '/Desk_moloni_model.php');
class Desk_moloni_sync_log_model extends Desk_moloni_model
{
/**
* Table name - must match Perfex CRM naming convention
*/
private $table = 'tbldeskmoloni_sync_log';
/**
* Valid operation types
*/
private $validOperationTypes = [
'create', 'update', 'delete', 'status_change', 'client_portal_access'
];
/**
* Valid entity types
*/
private $validEntityTypes = [
'client', 'product', 'invoice', 'estimate', 'credit_note', 'document'
];
/**
* Valid directions
*/
private $validDirections = [
'perfex_to_moloni', 'moloni_to_perfex', 'client_portal'
];
/**
* Valid status values
*/
private $validStatuses = [
'success', 'error', 'warning'
];
public function __construct()
{
parent::__construct();
// Use Perfex CRM table naming convention: tbl + module_prefix + table_name
$this->table = 'tbldeskmoloni_sync_log';
}
/**
* Log synchronization operation
*
* @param string $operationType Operation type (create, update, delete, status_change)
* @param string $entityType Entity type
* @param int|null $perfexId Perfex entity ID
* @param int|null $moloniId Moloni entity ID
* @param string $direction Sync direction
* @param string $status Operation status (success, error, warning)
* @param array|null $requestData Request data
* @param array|null $responseData Response data
* @param string|null $errorMessage Error message if applicable
* @param int|null $executionTimeMs Execution time in milliseconds
* @return int|false Log entry ID or false on failure
*/
public function logOperation(
$operationType,
$entityType,
$perfexId,
$moloniId,
$direction,
$status,
$requestData = null,
$responseData = null,
$errorMessage = null,
$executionTimeMs = null
) {
try {
$data = [
'operation_type' => $operationType,
'entity_type' => $entityType,
'perfex_id' => $perfexId ? (int)$perfexId : null,
'moloni_id' => $moloniId ? (int)$moloniId : null,
'direction' => $direction,
'status' => $status,
'request_data' => $requestData ? json_encode($requestData) : null,
'response_data' => $responseData ? json_encode($responseData) : null,
'error_message' => $errorMessage,
'execution_time_ms' => $executionTimeMs ? (int)$executionTimeMs : null,
'created_at' => date('Y-m-d H:i:s')
];
// Validate data
$validationErrors = $this->validateLogData($data);
if (!empty($validationErrors)) {
throw new Exception('Validation failed: ' . implode(', ', $validationErrors));
}
$result = $this->db->insert($this->table, $data);
if ($result) {
return $this->db->insert_id();
}
return false;
} catch (Exception $e) {
log_message('error', 'Desk-Moloni sync log error: ' . $e->getMessage());
return false;
}
}
/**
* Log successful operation
*
* @param string $operationType Operation type
* @param string $entityType Entity type
* @param int|null $perfexId Perfex entity ID
* @param int|null $moloniId Moloni entity ID
* @param string $direction Sync direction
* @param array|null $requestData Request data
* @param array|null $responseData Response data
* @param int|null $executionTimeMs Execution time in milliseconds
* @return int|false Log entry ID or false on failure
*/
public function logSuccess(
$operationType,
$entityType,
$perfexId,
$moloniId,
$direction,
$requestData = null,
$responseData = null,
$executionTimeMs = null
) {
return $this->logOperation(
$operationType,
$entityType,
$perfexId,
$moloniId,
$direction,
'success',
$requestData,
$responseData,
null,
$executionTimeMs
);
}
/**
* Log error operation
*
* @param string $operationType Operation type
* @param string $entityType Entity type
* @param int|null $perfexId Perfex entity ID
* @param int|null $moloniId Moloni entity ID
* @param string $direction Sync direction
* @param string $errorMessage Error message
* @param array|null $requestData Request data
* @param array|null $responseData Response data
* @param int|null $executionTimeMs Execution time in milliseconds
* @return int|false Log entry ID or false on failure
*/
public function logError(
$operationType,
$entityType,
$perfexId,
$moloniId,
$direction,
$errorMessage,
$requestData = null,
$responseData = null,
$executionTimeMs = null
) {
return $this->logOperation(
$operationType,
$entityType,
$perfexId,
$moloniId,
$direction,
'error',
$requestData,
$responseData,
$errorMessage,
$executionTimeMs
);
}
/**
* Log warning operation
*
* @param string $operationType Operation type
* @param string $entityType Entity type
* @param int|null $perfexId Perfex entity ID
* @param int|null $moloniId Moloni entity ID
* @param string $direction Sync direction
* @param string $warningMessage Warning message
* @param array|null $requestData Request data
* @param array|null $responseData Response data
* @param int|null $executionTimeMs Execution time in milliseconds
* @return int|false Log entry ID or false on failure
*/
public function logWarning(
$operationType,
$entityType,
$perfexId,
$moloniId,
$direction,
$warningMessage,
$requestData = null,
$responseData = null,
$executionTimeMs = null
) {
return $this->logOperation(
$operationType,
$entityType,
$perfexId,
$moloniId,
$direction,
'warning',
$requestData,
$responseData,
$warningMessage,
$executionTimeMs
);
}
/**
* Get log entries by entity
*
* @param string $entityType Entity type
* @param int|null $perfexId Perfex entity ID
* @param int|null $moloniId Moloni entity ID
* @param int $limit Maximum number of entries
* @return array Array of log entries
*/
public function getLogsByEntity($entityType, $perfexId = null, $moloniId = null, $limit = 50)
{
try {
$this->db->where('entity_type', $entityType);
if ($perfexId !== null) {
$this->db->where('perfex_id', (int)$perfexId);
}
if ($moloniId !== null) {
$this->db->where('moloni_id', (int)$moloniId);
}
$query = $this->db->order_by('created_at', 'DESC')
->limit($limit)
->get($this->table);
return $query->result();
} catch (Exception $e) {
log_message('error', 'Desk-Moloni sync log get by entity error: ' . $e->getMessage());
return [];
}
}
/**
* Get error logs within date range
*
* @param string $startDate Start date (Y-m-d H:i:s)
* @param string $endDate End date (Y-m-d H:i:s)
* @param int $limit Maximum number of entries
* @return array Array of error log entries
*/
public function getErrorLogs($startDate = null, $endDate = null, $limit = 100)
{
try {
$this->db->where('status', 'error');
if ($startDate !== null) {
$this->db->where('created_at >=', $startDate);
}
if ($endDate !== null) {
$this->db->where('created_at <=', $endDate);
}
$query = $this->db->order_by('created_at', 'DESC')
->limit($limit)
->get($this->table);
return $query->result();
} catch (Exception $e) {
log_message('error', 'Desk-Moloni sync log get errors error: ' . $e->getMessage());
return [];
}
}
/**
* Get performance statistics
*
* @param string $startDate Start date for analysis
* @param string $endDate End date for analysis
* @return array Performance statistics
*/
public function getPerformanceStats($startDate = null, $endDate = null)
{
try {
if ($startDate === null) {
$startDate = date('Y-m-d H:i:s', strtotime('-24 hours'));
}
if ($endDate === null) {
$endDate = date('Y-m-d H:i:s');
}
$stats = [];
// Overall statistics
$query = $this->db->select('
COUNT(*) as total_operations,
AVG(execution_time_ms) as avg_execution_time,
MAX(execution_time_ms) as max_execution_time,
MIN(execution_time_ms) as min_execution_time
')
->where('created_at >=', $startDate)
->where('created_at <=', $endDate)
->where('execution_time_ms IS NOT NULL')
->get($this->table);
$stats['overall'] = $query->row_array();
// By status
foreach ($this->validStatuses as $status) {
$count = $this->db->where('status', $status)
->where('created_at >=', $startDate)
->where('created_at <=', $endDate)
->count_all_results($this->table);
$stats['by_status'][$status] = $count;
}
// By entity type
foreach ($this->validEntityTypes as $entityType) {
$count = $this->db->where('entity_type', $entityType)
->where('created_at >=', $startDate)
->where('created_at <=', $endDate)
->count_all_results($this->table);
$stats['by_entity'][$entityType] = $count;
}
// By operation type
foreach ($this->validOperationTypes as $operationType) {
$count = $this->db->where('operation_type', $operationType)
->where('created_at >=', $startDate)
->where('created_at <=', $endDate)
->count_all_results($this->table);
$stats['by_operation'][$operationType] = $count;
}
// By direction
foreach ($this->validDirections as $direction) {
$count = $this->db->where('direction', $direction)
->where('created_at >=', $startDate)
->where('created_at <=', $endDate)
->count_all_results($this->table);
$stats['by_direction'][$direction] = $count;
}
// Slow operations (> 5 seconds)
$stats['slow_operations'] = $this->db->where('execution_time_ms >', 5000)
->where('created_at >=', $startDate)
->where('created_at <=', $endDate)
->count_all_results($this->table);
return $stats;
} catch (Exception $e) {
log_message('error', 'Desk-Moloni sync log performance stats error: ' . $e->getMessage());
return [];
}
}
/**
* Get recent activity summary
*
* @param int $hours Number of hours to look back
* @param int $limit Maximum number of entries
* @return array Recent activity log entries
*/
public function getRecentActivity($hours = 24, $limit = 50)
{
try {
$startDate = date('Y-m-d H:i:s', strtotime("-{$hours} hours"));
$query = $this->db->where('created_at >=', $startDate)
->order_by('created_at', 'DESC')
->limit($limit)
->get($this->table);
return $query->result();
} catch (Exception $e) {
log_message('error', 'Desk-Moloni sync log recent activity error: ' . $e->getMessage());
return [];
}
}
/**
* Clean up old log entries
*
* @param int $olderThanDays Delete logs older than X days
* @param bool $keepErrors Whether to keep error logs longer
* @return int Number of entries deleted
*/
public function cleanupOldLogs($olderThanDays = 365, $keepErrors = true)
{
try {
$cutoffDate = date('Y-m-d H:i:s', strtotime("-{$olderThanDays} days"));
$this->db->where('created_at <', $cutoffDate);
if ($keepErrors) {
// Don't delete error logs
$this->db->where('status !=', 'error');
}
$result = $this->db->delete($this->table);
return $this->db->affected_rows();
} catch (Exception $e) {
log_message('error', 'Desk-Moloni sync log cleanup error: ' . $e->getMessage());
return 0;
}
}
/**
* Get log entry by ID
*
* @param int $logId Log entry ID
* @return object|null Log entry or null if not found
*/
public function getLogById($logId)
{
try {
$query = $this->db->where('id', (int)$logId)->get($this->table);
return $query->num_rows() > 0 ? $query->row() : null;
} catch (Exception $e) {
log_message('error', 'Desk-Moloni sync log get by ID error: ' . $e->getMessage());
return null;
}
}
/**
* Search logs by criteria
*
* @param array $criteria Search criteria
* @param int $limit Maximum number of results
* @param int $offset Offset for pagination
* @return array Search results
*/
public function searchLogs($criteria, $limit = 50, $offset = 0)
{
try {
// Entity type filter
if (!empty($criteria['entity_type'])) {
$this->db->where('entity_type', $criteria['entity_type']);
}
// Status filter
if (!empty($criteria['status'])) {
$this->db->where('status', $criteria['status']);
}
// Operation type filter
if (!empty($criteria['operation_type'])) {
$this->db->where('operation_type', $criteria['operation_type']);
}
// Direction filter
if (!empty($criteria['direction'])) {
$this->db->where('direction', $criteria['direction']);
}
// Date range filter
if (!empty($criteria['start_date'])) {
$this->db->where('created_at >=', $criteria['start_date']);
}
if (!empty($criteria['end_date'])) {
$this->db->where('created_at <=', $criteria['end_date']);
}
// Entity ID filters
if (!empty($criteria['perfex_id'])) {
$this->db->where('perfex_id', (int)$criteria['perfex_id']);
}
if (!empty($criteria['moloni_id'])) {
$this->db->where('moloni_id', (int)$criteria['moloni_id']);
}
// Error message search
if (!empty($criteria['error_message'])) {
$this->db->like('error_message', $criteria['error_message']);
}
// Execution time filter
if (!empty($criteria['min_execution_time'])) {
$this->db->where('execution_time_ms >=', (int)$criteria['min_execution_time']);
}
if (!empty($criteria['max_execution_time'])) {
$this->db->where('execution_time_ms <=', (int)$criteria['max_execution_time']);
}
$query = $this->db->order_by('created_at', 'DESC')
->limit($limit, $offset)
->get($this->table);
return $query->result();
} catch (Exception $e) {
log_message('error', 'Desk-Moloni sync log search error: ' . $e->getMessage());
return [];
}
}
/**
* Export logs to CSV format
*
* @param array $criteria Search criteria
* @param int $limit Maximum number of records
* @return string CSV data
*/
public function exportToCsv($criteria = [], $limit = 1000)
{
try {
$logs = $this->searchLogs($criteria, $limit);
if (empty($logs)) {
return '';
}
$csv = [];
// Headers
$csv[] = [
'ID', 'Operation Type', 'Entity Type', 'Perfex ID', 'Moloni ID',
'Direction', 'Status', 'Error Message', 'Execution Time (ms)', 'Created At'
];
// Data rows
foreach ($logs as $log) {
$csv[] = [
$log->id,
$log->operation_type,
$log->entity_type,
$log->perfex_id ?: '',
$log->moloni_id ?: '',
$log->direction,
$log->status,
$log->error_message ?: '',
$log->execution_time_ms ?: '',
$log->created_at
];
}
// Convert to CSV string
$output = '';
foreach ($csv as $row) {
$output .= '"' . implode('","', $row) . '"' . "\n";
}
return $output;
} catch (Exception $e) {
log_message('error', 'Desk-Moloni sync log export error: ' . $e->getMessage());
return '';
}
}
/**
* Validate log data
*
* @param array $data Log data to validate
* @return array Validation errors
*/
private function validateLogData($data)
{
$errors = [];
// Required fields
$requiredFields = ['operation_type', 'entity_type', 'direction', 'status'];
$errors = array_merge($errors, $this->validateRequiredFields($data, $requiredFields));
// Operation type validation
if (isset($data['operation_type']) && !$this->validateEnum($data['operation_type'], $this->validOperationTypes)) {
$errors[] = 'Invalid operation type. Must be one of: ' . implode(', ', $this->validOperationTypes);
}
// Entity type validation
if (isset($data['entity_type']) && !$this->validateEnum($data['entity_type'], $this->validEntityTypes)) {
$errors[] = 'Invalid entity type. Must be one of: ' . implode(', ', $this->validEntityTypes);
}
// Direction validation
if (isset($data['direction']) && !$this->validateEnum($data['direction'], $this->validDirections)) {
$errors[] = 'Invalid direction. Must be one of: ' . implode(', ', $this->validDirections);
}
// Status validation
if (isset($data['status']) && !$this->validateEnum($data['status'], $this->validStatuses)) {
$errors[] = 'Invalid status. Must be one of: ' . implode(', ', $this->validStatuses);
}
// Entity ID validation - at least one must be present
if (empty($data['perfex_id']) && empty($data['moloni_id'])) {
$errors[] = 'At least one of perfex_id or moloni_id must be provided';
}
// Execution time validation
if (isset($data['execution_time_ms']) && $data['execution_time_ms'] !== null) {
if (!is_numeric($data['execution_time_ms']) || (int)$data['execution_time_ms'] < 0) {
$errors[] = 'Execution time must be a non-negative integer';
}
}
// JSON validation
if (isset($data['request_data']) && !$this->validateJSON($data['request_data'])) {
$errors[] = 'Request data must be valid JSON';
}
if (isset($data['response_data']) && !$this->validateJSON($data['response_data'])) {
$errors[] = 'Response data must be valid JSON';
}
return $errors;
}
/**
* Get valid operation types
*
* @return array Valid operation types
*/
public function getValidOperationTypes()
{
return $this->validOperationTypes;
}
/**
* Get valid entity types
*
* @return array Valid entity types
*/
public function getValidEntityTypes()
{
return $this->validEntityTypes;
}
/**
* Get valid directions
*
* @return array Valid directions
*/
public function getValidDirections()
{
return $this->validDirections;
}
/**
* Get valid status values
*
* @return array Valid status values
*/
public function getValidStatuses()
{
return $this->validStatuses;
}
/**
* Log client portal access for audit trail
*
* @param array $logData Client portal access data
* @return int|false Log entry ID or false on failure
*/
public function logClientPortalAccess($logData)
{
try {
$data = [
'operation_type' => 'client_portal_access',
'entity_type' => 'document',
'perfex_id' => $logData['document_id'] ?? null,
'moloni_id' => null,
'direction' => 'client_portal',
'status' => $logData['status'] ?? 'success',
'request_data' => json_encode([
'client_id' => $logData['client_id'],
'action' => $logData['action'],
'ip_address' => $logData['ip_address'],
'user_agent' => $logData['user_agent']
]),
'response_data' => null,
'error_message' => $logData['error_message'] ?? null,
'execution_time_ms' => null,
'created_at' => $logData['timestamp'] ?? date('Y-m-d H:i:s')
];
return $this->db->insert($this->table, $data) ? $this->db->insert_id() : false;
} catch (Exception $e) {
log_message('error', 'Client portal access log error: ' . $e->getMessage());
return false;
}
}
/**
* Get client portal access logs
*
* @param int $clientId Client ID
* @param array $filters Optional filters
* @param int $limit Maximum number of entries
* @return array Client portal access logs
*/
public function getClientPortalAccessLogs($clientId, array $filters = [], $limit = 50)
{
try {
$this->db->where('operation_type', 'client_portal_access');
$this->db->like('request_data', '"client_id":' . $clientId);
// Apply filters
if (isset($filters['action'])) {
$this->db->like('request_data', '"action":"' . $filters['action'] . '"');
}
if (isset($filters['status'])) {
$this->db->where('status', $filters['status']);
}
if (isset($filters['start_date'])) {
$this->db->where('created_at >=', $filters['start_date']);
}
if (isset($filters['end_date'])) {
$this->db->where('created_at <=', $filters['end_date']);
}
$query = $this->db->order_by('created_at', 'DESC')
->limit($limit)
->get($this->table);
return $query->result();
} catch (Exception $e) {
log_message('error', 'Get client portal access logs error: ' . $e->getMessage());
return [];
}
}
/**
* Get client portal access statistics
*
* @param int $clientId Client ID
* @param string $period Period for statistics (day, week, month)
* @return array Access statistics
*/
public function getClientPortalAccessStats($clientId, $period = 'week')
{
try {
$startDate = date('Y-m-d H:i:s', strtotime('-1 ' . $period));
$stats = [
'total_accesses' => 0,
'successful_accesses' => 0,
'failed_accesses' => 0,
'actions' => [],
'documents_accessed' => 0
];
// Total accesses
$stats['total_accesses'] = $this->db->where('operation_type', 'client_portal_access')
->like('request_data', '"client_id":' . $clientId)
->where('created_at >=', $startDate)
->count_all_results($this->table);
// Successful accesses
$stats['successful_accesses'] = $this->db->where('operation_type', 'client_portal_access')
->like('request_data', '"client_id":' . $clientId)
->where('status', 'success')
->where('created_at >=', $startDate)
->count_all_results($this->table);
// Failed accesses
$stats['failed_accesses'] = $stats['total_accesses'] - $stats['successful_accesses'];
// Actions breakdown
$logs = $this->getClientPortalAccessLogs($clientId, ['start_date' => $startDate], 1000);
$actionCounts = [];
$documentIds = [];
foreach ($logs as $log) {
$requestData = json_decode($log->request_data, true);
if ($requestData && isset($requestData['action'])) {
$action = $requestData['action'];
$actionCounts[$action] = ($actionCounts[$action] ?? 0) + 1;
if ($log->perfex_id) {
$documentIds[] = $log->perfex_id;
}
}
}
$stats['actions'] = $actionCounts;
$stats['documents_accessed'] = count(array_unique($documentIds));
return $stats;
} catch (Exception $e) {
log_message('error', 'Get client portal access stats error: ' . $e->getMessage());
return [];
}
}
/**
* Clean up old client portal logs
*
* @param int $olderThanDays Delete logs older than X days
* @return int Number of entries deleted
*/
public function cleanupClientPortalLogs($olderThanDays = 90)
{
try {
$cutoffDate = date('Y-m-d H:i:s', strtotime("-{$olderThanDays} days"));
$this->db->where('operation_type', 'client_portal_access')
->where('created_at <', $cutoffDate);
$result = $this->db->delete($this->table);
return $this->db->affected_rows();
} catch (Exception $e) {
log_message('error', 'Cleanup client portal logs error: ' . $e->getMessage());
return 0;
}
}
/**
* Get logs with related data using JOIN queries (optimized)
* Prevents N+1 query problem
*
* @param int $limit Number of records to return
* @param int $offset Starting offset
* @param array $filters Additional filters
* @return array Logs with related data
*/
public function get_logs_with_details($limit = 50, $offset = 0, $filters = [])
{
// Align to actual schema/columns and table names (tbldeskmoloni_*)
$this->db->select('
sl.id,
sl.entity_type,
sl.perfex_id as entity_id,
sl.operation_type as action,
sl.status,
sl.error_message,
sl.execution_time_ms as execution_time,
sl.created_at,
dm.moloni_id,
dm.perfex_id as mapping_perfex_id,
dm.entity_type as mapping_entity_type,
dm.sync_status as mapping_sync_status,
dm.last_sync_at as mapping_last_sync
');
$this->db->from($this->table . ' sl');
// LEFT JOINs to include related data
$this->db->join('tbldeskmoloni_sync_queue sq', '1=0', 'left'); // queue relation not available in schema
$this->db->join('tbldeskmoloni_mapping dm', 'sl.perfex_id = dm.perfex_id AND sl.entity_type = dm.entity_type', 'left');
// Apply filters
if (!empty($filters['entity_type'])) {
$this->db->where('sl.entity_type', $filters['entity_type']);
}
if (!empty($filters['status'])) {
$this->db->where('sl.status', $filters['status']);
}
if (!empty($filters['date_from'])) {
$this->db->where('sl.created_at >=', $filters['date_from']);
}
if (!empty($filters['date_to'])) {
$this->db->where('sl.created_at <=', $filters['date_to']);
}
// Ordering and pagination
$this->db->order_by('sl.created_at', 'DESC');
$this->db->limit($limit, $offset);
$query = $this->db->get();
$results = $query->result_array();
desk_moloni_log('debug', "Fetched logs with details", [
'count' => count($results),
'limit' => $limit,
'offset' => $offset,
'filters' => $filters
], 'performance');
return $results;
}
/**
* Get sync statistics with single query
*
* @param array $filters Date range and entity filters
* @return array Statistics grouped by status, entity type, etc.
*/
public function get_sync_statistics($filters = [])
{
// Build statistics query
$this->db->select('
COUNT(*) as total_syncs,
SUM(CASE WHEN status = "success" THEN 1 ELSE 0 END) as successful_syncs,
SUM(CASE WHEN status = "error" THEN 1 ELSE 0 END) as failed_syncs,
SUM(CASE WHEN status = "pending" THEN 1 ELSE 0 END) as pending_syncs,
AVG(execution_time_ms) as avg_execution_time,
MAX(execution_time_ms) as max_execution_time,
MIN(execution_time_ms) as min_execution_time,
entity_type,
DATE(created_at) as sync_date
');
$this->db->from($this->table);
// Apply date filters
if (!empty($filters['date_from'])) {
$this->db->where('created_at >=', $filters['date_from']);
}
if (!empty($filters['date_to'])) {
$this->db->where('created_at <=', $filters['date_to']);
}
$this->db->group_by(['entity_type', 'DATE(created_at)']);
$this->db->order_by('sync_date', 'DESC');
$query = $this->db->get();
return $query->result_array();
}
/**
* Get recent activity with minimal data for dashboard
* Optimized for speed
*
* @param int $limit Number of recent activities
* @return array Recent sync activities
*/
public function get_recent_activity($limit = 10)
{
try {
// Check if table exists first
if (!$this->db->table_exists($this->table)) {
log_message('info', 'Desk-Moloni sync log table does not exist yet');
return [];
}
$this->db->reset_query();
$this->db->select('
entity_type,
perfex_id as entity_id,
operation_type as action,
status,
created_at,
execution_time_ms,
direction,
moloni_id
');
$this->db->from($this->table);
$this->db->where('created_at >', date('Y-m-d H:i:s', strtotime('-24 hours')));
$this->db->order_by('created_at', 'DESC');
$this->db->limit($limit);
$query = $this->db->get();
return $query->result_array();
} catch (Exception $e) {
log_message('error', 'Desk-Moloni get_recent_activity error: ' . $e->getMessage());
return [];
}
}
}