/** * Descomplicar® Crescimento Digital * https://descomplicar.pt */ load->model('desk_moloni/desk_moloni_sync_log_model', 'sync_log_model'); $this->load->model('desk_moloni/desk_moloni_config_model', 'config_model'); } /** * Logs viewing interface */ public function index() { if (!has_permission('desk_moloni', '', 'view')) { access_denied('desk_moloni'); } $data = [ 'title' => _l('desk_moloni_sync_logs'), 'entity_types' => ['client', 'product', 'invoice', 'estimate', 'credit_note'], 'log_stats' => method_exists($this->sync_log_model, 'get_log_statistics') ? $this->sync_log_model->get_log_statistics() : [] ]; $this->load->view('admin/includes/header', $data); $this->load->view('admin/modules/desk_moloni/logs', $data); $this->load->view('admin/includes/footer'); } /** * Get logs with filtering and pagination */ public function get_logs() { if (!has_permission('desk_moloni', '', 'view')) { $this->output ->set_status_header(403) ->set_content_type('application/json') ->set_output(json_encode(['success' => false, 'message' => _l('access_denied')])); return; } try { $filters = [ 'entity_type' => $this->input->get('entity_type'), 'status' => $this->input->get('status'), 'operation_type' => $this->input->get('operation_type'), 'direction' => $this->input->get('direction'), 'from_date' => $this->input->get('from_date'), 'to_date' => $this->input->get('to_date'), 'search' => $this->input->get('search') ]; $pagination = [ 'limit' => (int) $this->input->get('limit') ?: 100, 'offset' => (int) $this->input->get('offset') ?: 0 ]; $sort = [ 'field' => $this->input->get('sort_field') ?: 'created_at', 'direction' => $this->input->get('sort_direction') ?: 'desc' ]; $log_data = $this->sync_log_model->get_filtered_logs($filters, $pagination, $sort); $this->output ->set_content_type('application/json') ->set_output(json_encode([ 'success' => true, 'data' => [ 'total' => $log_data['total'], 'logs' => $log_data['logs'], 'pagination' => [ 'current_page' => floor($pagination['offset'] / $pagination['limit']) + 1, 'per_page' => $pagination['limit'], 'total_items' => $log_data['total'], 'total_pages' => ceil($log_data['total'] / $pagination['limit']) ] ] ])); } catch (Exception $e) { log_message('error', 'Desk-Moloni get logs error: ' . $e->getMessage()); $this->output ->set_status_header(500) ->set_content_type('application/json') ->set_output(json_encode([ 'success' => false, 'message' => $e->getMessage() ])); } } /** * Get detailed log entry */ public function get_log_details($log_id) { if (!has_permission('desk_moloni', '', 'view')) { $this->output ->set_status_header(403) ->set_content_type('application/json') ->set_output(json_encode(['success' => false, 'message' => _l('access_denied')])); return; } try { $log_id = (int) $log_id; if (!$log_id) { throw new Exception(_l('desk_moloni_invalid_log_id')); } $log = method_exists($this->sync_log_model, 'get_log_details') ? $this->sync_log_model->get_log_details($log_id) : null; if (!$log) { throw new Exception(_l('desk_moloni_log_not_found')); } $this->output ->set_content_type('application/json') ->set_output(json_encode([ 'success' => true, 'data' => $log ])); } catch (Exception $e) { log_message('error', 'Desk-Moloni get log details error: ' . $e->getMessage()); $this->output ->set_status_header(400) ->set_content_type('application/json') ->set_output(json_encode([ 'success' => false, 'message' => $e->getMessage() ])); } } /** * Get log statistics */ public function get_statistics() { if (!has_permission('desk_moloni', '', 'view')) { $this->output ->set_status_header(403) ->set_content_type('application/json') ->set_output(json_encode(['success' => false, 'message' => _l('access_denied')])); return; } try { $days = (int) $this->input->get('days') ?: 7; $entity_type = $this->input->get('entity_type'); $statistics = [ 'summary' => $this->sync_log_model->get_log_summary($days, $entity_type), 'trends' => $this->sync_log_model->get_log_trends($days, $entity_type), 'top_errors' => $this->sync_log_model->get_top_errors($days, 10), 'performance_stats' => $this->sync_log_model->get_performance_statistics($days, $entity_type) ]; $this->output ->set_content_type('application/json') ->set_output(json_encode([ 'success' => true, 'data' => $statistics ])); } catch (Exception $e) { log_message('error', 'Desk-Moloni log statistics error: ' . $e->getMessage()); $this->output ->set_status_header(500) ->set_content_type('application/json') ->set_output(json_encode([ 'success' => false, 'message' => $e->getMessage() ])); } } /** * Export logs */ public function export() { if (!has_permission('desk_moloni_view', '', 'view')) { access_denied('desk_moloni'); } try { $format = $this->input->get('format') ?: 'csv'; $filters = [ 'entity_type' => $this->input->get('entity_type'), 'status' => $this->input->get('status'), 'operation_type' => $this->input->get('operation_type'), 'direction' => $this->input->get('direction'), 'from_date' => $this->input->get('from_date'), 'to_date' => $this->input->get('to_date'), 'search' => $this->input->get('search') ]; $logs = $this->sync_log_model->get_logs_for_export($filters); if ($format === 'json') { $this->_export_as_json($logs); } else { $this->_export_as_csv($logs); } } catch (Exception $e) { log_message('error', 'Desk-Moloni export logs error: ' . $e->getMessage()); set_alert('danger', $e->getMessage()); redirect(admin_url('modules/desk_moloni/logs')); } } /** * Clear old logs */ public function clear_old_logs() { if (!has_permission('desk_moloni', '', 'delete')) { $this->output ->set_status_header(403) ->set_content_type('application/json') ->set_output(json_encode(['success' => false, 'message' => _l('access_denied')])); return; } try { $days_old = (int) $this->input->post('days_old') ?: 30; $keep_errors = $this->input->post('keep_errors') === '1'; $deleted_count = $this->sync_log_model->clear_old_logs($days_old, $keep_errors); $this->output ->set_content_type('application/json') ->set_output(json_encode([ 'success' => true, 'message' => sprintf( _l('desk_moloni_logs_cleared'), $deleted_count ), 'data' => ['deleted_count' => $deleted_count] ])); } catch (Exception $e) { log_message('error', 'Desk-Moloni clear logs error: ' . $e->getMessage()); $this->output ->set_status_header(500) ->set_content_type('application/json') ->set_output(json_encode([ 'success' => false, 'message' => $e->getMessage() ])); } } /** * Get error analysis */ public function get_error_analysis() { if (!has_permission('desk_moloni_view', '', 'view')) { $this->output ->set_status_header(403) ->set_content_type('application/json') ->set_output(json_encode(['success' => false, 'message' => _l('access_denied')])); return; } try { $days = (int) $this->input->get('days') ?: 7; $analysis = [ 'error_categories' => $this->sync_log_model->get_error_categories($days), 'error_trends' => $this->sync_log_model->get_error_trends($days), 'frequent_errors' => $this->sync_log_model->get_frequent_errors($days, 20), 'error_by_entity' => $this->sync_log_model->get_errors_by_entity($days), 'resolution_suggestions' => $this->_get_resolution_suggestions() ]; $this->output ->set_content_type('application/json') ->set_output(json_encode([ 'success' => true, 'data' => $analysis ])); } catch (Exception $e) { log_message('error', 'Desk-Moloni error analysis error: ' . $e->getMessage()); $this->output ->set_status_header(500) ->set_content_type('application/json') ->set_output(json_encode([ 'success' => false, 'message' => $e->getMessage() ])); } } /** * Search logs */ public function search() { if (!has_permission('desk_moloni_view', '', 'view')) { $this->output ->set_status_header(403) ->set_content_type('application/json') ->set_output(json_encode(['success' => false, 'message' => _l('access_denied')])); return; } try { $query = $this->input->get('q'); $limit = (int) $this->input->get('limit') ?: 50; if (empty($query) || strlen($query) < 3) { throw new Exception(_l('desk_moloni_search_query_too_short')); } $results = $this->sync_log_model->search_logs($query, $limit); $this->output ->set_content_type('application/json') ->set_output(json_encode([ 'success' => true, 'data' => $results ])); } catch (Exception $e) { log_message('error', 'Desk-Moloni search logs error: ' . $e->getMessage()); $this->output ->set_status_header(400) ->set_content_type('application/json') ->set_output(json_encode([ 'success' => false, 'message' => $e->getMessage() ])); } } /** * Export logs as CSV */ private function _export_as_csv($logs) { $filename = 'desk_moloni_logs_' . date('Y-m-d_H-i-s') . '.csv'; header('Content-Type: text/csv'); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Cache-Control: no-cache, must-revalidate'); header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); $output = fopen('php://output', 'w'); // CSV headers $headers = [ 'ID', 'Timestamp', 'Operation', 'Entity Type', 'Perfex ID', 'Moloni ID', 'Direction', 'Status', 'Execution Time (ms)', 'Error Message' ]; fputcsv($output, $headers); // CSV data foreach ($logs as $log) { $row = [ $log['id'], $log['created_at'], $log['operation_type'], $log['entity_type'], $log['perfex_id'] ?: '', $log['moloni_id'] ?: '', $log['direction'], $log['status'], $log['execution_time_ms'] ?: '', $log['error_message'] ?: '' ]; fputcsv($output, $row); } fclose($output); exit; } /** * Export logs as JSON */ private function _export_as_json($logs) { $filename = 'desk_moloni_logs_' . date('Y-m-d_H-i-s') . '.json'; header('Content-Type: application/json'); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Cache-Control: no-cache, must-revalidate'); header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); echo json_encode([ 'export_date' => date('Y-m-d H:i:s'), 'total_records' => count($logs), 'logs' => $logs ], JSON_PRETTY_PRINT); exit; } /** * Get resolution suggestions for common errors */ private function _get_resolution_suggestions() { return [ 'authentication_failed' => [ 'title' => _l('desk_moloni_auth_error_title'), 'description' => _l('desk_moloni_auth_error_desc'), 'actions' => [ _l('desk_moloni_refresh_oauth_token'), _l('desk_moloni_check_api_credentials') ] ], 'rate_limit_exceeded' => [ 'title' => _l('desk_moloni_rate_limit_title'), 'description' => _l('desk_moloni_rate_limit_desc'), 'actions' => [ _l('desk_moloni_reduce_sync_frequency'), _l('desk_moloni_implement_backoff') ] ], 'validation_error' => [ 'title' => _l('desk_moloni_validation_error_title'), 'description' => _l('desk_moloni_validation_error_desc'), 'actions' => [ _l('desk_moloni_check_required_fields'), _l('desk_moloni_verify_data_format') ] ], 'network_error' => [ 'title' => _l('desk_moloni_network_error_title'), 'description' => _l('desk_moloni_network_error_desc'), 'actions' => [ _l('desk_moloni_check_connectivity'), _l('desk_moloni_verify_firewall') ] ] ]; } }