HEX
Server: LiteSpeed
System: Linux cp01.bhostbrasil.com.br 5.14.0-611.16.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Mon Dec 22 03:40:39 EST 2025 x86_64
User: onlyfibr (1083)
PHP: 8.2.31
Disabled: NONE
Upload Files
File: /home/onlyfibr/public_html/assinar/admin/relatorios.php
<?php
// HABILITAR ERROS PARA DEBUG - REMOVER/COMENTAR EM PRODUÇÃO!
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// ---------------------------------------------------------
// 1. Verifica se o admin está logado
require_once 'includes/auth_check.php';

// Define o título desta página ANTES de incluir o header
$pageTitle = 'Relatórios';

require_once '../includes/config/config.php'; // Necessário para $pdo e constantes
require_once '../includes/functions/functions.php'; // Pode ser necessário para alguma função auxiliar

// --- Processamento de Solicitações de Relatórios ---
$mensagem_erro = $_SESSION['erro_dashboard'] ?? null;
$mensagem_sucesso = $_SESSION['sucesso_dashboard'] ?? null;
unset($_SESSION['erro_dashboard'], $_SESSION['sucesso_dashboard']);

// Inicializa variáveis para estatísticas
$relatorio_data = [];
$graficos_data = [];
$filtro_data_inicio = $_GET['data_inicio'] ?? date('Y-m-d', strtotime('-30 days'));
$filtro_data_fim = $_GET['data_fim'] ?? date('Y-m-d');
$filtro_status = $_GET['status'] ?? '';

// --- Processamento do Formulário (Geração de Relatório) ---
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['gerar_relatorio'])) {
    // ADICIONAR VALIDAÇÃO CSRF TOKEN AQUI !!!
    
    // Captura e valida os filtros
    $filtro_data_inicio = filter_input(INPUT_POST, 'data_inicio', FILTER_SANITIZE_STRING) ?? date('Y-m-d', strtotime('-30 days'));
    $filtro_data_fim = filter_input(INPUT_POST, 'data_fim', FILTER_SANITIZE_STRING) ?? date('Y-m-d');
    $filtro_status = filter_input(INPUT_POST, 'status', FILTER_SANITIZE_STRING) ?? '';
    
    // Validação simples de datas
    if (strtotime($filtro_data_inicio) > strtotime($filtro_data_fim)) {
        $mensagem_erro = "Data inicial não pode ser maior que a data final.";
    }
}

// --- Geração dos Relatórios ---
if ($pdo) {
    try {
        // Condições básicas para a consulta
        $where_conditions = ["1=1"]; // Sempre verdadeiro para facilitar concatenação de ANDs
        $query_params = [];
        
        // Adiciona condições com base nos filtros
        if (!empty($filtro_data_inicio) && !empty($filtro_data_fim)) {
            $where_conditions[] = "data_envio BETWEEN :data_inicio AND :data_fim";
            $query_params[':data_inicio'] = $filtro_data_inicio . ' 00:00:00';
            $query_params[':data_fim'] = $filtro_data_fim . ' 23:59:59';
        }
        
        if (!empty($filtro_status)) {
            $where_conditions[] = "status_processamento = :status";
            $query_params[':status'] = $filtro_status;
        }
        
        // Constrói a cláusula WHERE
        $where_clause = implode(' AND ', $where_conditions);
        
        // --------- Relatório 1: Cadastros por Status ---------
        $sql_status = "
            SELECT status_processamento, COUNT(*) as total
            FROM precadastros
            WHERE $where_clause
            GROUP BY status_processamento
            ORDER BY total DESC";
        
        $stmt_status = $pdo->prepare($sql_status);
        foreach ($query_params as $param => $value) {
            $stmt_status->bindValue($param, $value);
        }
        $stmt_status->execute();
        $relatorio_data['status'] = $stmt_status->fetchAll(PDO::FETCH_ASSOC);
        
        // Prepara dados para o gráfico de pizza de status
        $graficos_data['status_labels'] = [];
        $graficos_data['status_values'] = [];
        $graficos_data['status_colors'] = [];
        
        $colors = [
            'em_analise' => '#47076c',
            'aguardando_contato' => '#e86d0f',
            'aprovado' => '#046018',
            'reprovado' => '#c90a0a',
            'tentando_contato' => '#0b2bdf',
            'contrato_realizado_no_sgp' => '#28f404',
            'inviabilidade_tecnica' => '#fddf01'
        ];
        
        foreach ($relatorio_data['status'] as $status_item) {
            $status_key = $status_item['status_processamento'];
            $status_label = ucwords(str_replace('_', ' ', $status_key));
            
            $graficos_data['status_labels'][] = $status_label;
            $graficos_data['status_values'][] = (int)$status_item['total'];
            $graficos_data['status_colors'][] = $colors[$status_key] ?? '#999999';
        }
        
        // --------- Relatório 2: Cadastros por Dia ---------
        $sql_diario = "
            SELECT DATE(data_envio) as data, COUNT(*) as total
            FROM precadastros
            WHERE $where_clause
            GROUP BY DATE(data_envio)
            ORDER BY data ASC";
        
        $stmt_diario = $pdo->prepare($sql_diario);
        foreach ($query_params as $param => $value) {
            $stmt_diario->bindValue($param, $value);
        }
        $stmt_diario->execute();
        $relatorio_data['diario'] = $stmt_diario->fetchAll(PDO::FETCH_ASSOC);
        
        // Prepara dados para o gráfico de linha diário
        $graficos_data['diario_labels'] = [];
        $graficos_data['diario_values'] = [];
        
        foreach ($relatorio_data['diario'] as $dia_item) {
            // Formata a data para exibição (DD/MM/YYYY)
            $data_formatada = date('d/m/Y', strtotime($dia_item['data']));
            
            $graficos_data['diario_labels'][] = $data_formatada;
            $graficos_data['diario_values'][] = (int)$dia_item['total'];
        }
        
        // --------- Relatório 3: Top 5 Cidades ---------
        $sql_cidades = "
            SELECT cidade, estado, COUNT(*) as total
            FROM precadastros
            WHERE $where_clause
            GROUP BY cidade, estado
            ORDER BY total DESC
            LIMIT 5";
        
        $stmt_cidades = $pdo->prepare($sql_cidades);
        foreach ($query_params as $param => $value) {
            $stmt_cidades->bindValue($param, $value);
        }
        $stmt_cidades->execute();
        $relatorio_data['cidades'] = $stmt_cidades->fetchAll(PDO::FETCH_ASSOC);
        
        // Prepara dados para o gráfico de barras de cidades
        $graficos_data['cidades_labels'] = [];
        $graficos_data['cidades_values'] = [];
        
        foreach ($relatorio_data['cidades'] as $cidade_item) {
            $cidade_label = $cidade_item['cidade'] . '/' . $cidade_item['estado'];
            
            $graficos_data['cidades_labels'][] = $cidade_label;
            $graficos_data['cidades_values'][] = (int)$cidade_item['total'];
        }
        
        // --------- Relatório 4: Totais Gerais ---------
        // Total de cadastros no período
        $sql_total = "
            SELECT COUNT(*) as total
            FROM precadastros
            WHERE $where_clause";
        
        $stmt_total = $pdo->prepare($sql_total);
        foreach ($query_params as $param => $value) {
            $stmt_total->bindValue($param, $value);
        }
        $stmt_total->execute();
        $relatorio_data['total_geral'] = $stmt_total->fetchColumn();
        
        // Total pendente de processamento
        $sql_pendentes = "
            SELECT COUNT(*) as total
            FROM precadastros
            WHERE $where_clause AND p_processar = TRUE";
        
        $stmt_pendentes = $pdo->prepare($sql_pendentes);
        foreach ($query_params as $param => $value) {
            $stmt_pendentes->bindValue($param, $value);
        }
        $stmt_pendentes->execute();
        $relatorio_data['total_pendentes'] = $stmt_pendentes->fetchColumn();
        
        // Total processado com sucesso
        $sql_processados = "
            SELECT COUNT(*) as total
            FROM precadastros
            WHERE $where_clause AND p_processar = FALSE AND status_processamento = 'aprovado'";
        
        $stmt_processados = $pdo->prepare($sql_processados);
        foreach ($query_params as $param => $value) {
            $stmt_processados->bindValue($param, $value);
        }
        $stmt_processados->execute();
        $relatorio_data['total_processados'] = $stmt_processados->fetchColumn();
        
    } catch (\PDOException $e) {
        $mensagem_erro = "Erro ao gerar relatórios: " . $e->getMessage();
        error_log("Erro PDO ao gerar relatórios: " . $e->getMessage());
    }
} else {
    $mensagem_erro = "Erro crítico: Conexão com banco de dados falhou.";
}

// Lista de status para o filtro
$status_options = [
    'em_analise' => 'Em Análise',
    'aprovado' => 'Aprovado',
    'reprovado' => 'Reprovado',
    'aguardando_contato' => 'Aguardando Contato',
    'tentando_contato' => 'Tentando Contato',
    'inviabilidade_tecnica' => 'Inviabilidade Técnica',
    'contrato_realizado_no_sgp' => 'Contrato Realizado no SGP'
];

// Obter os menus dinamicamente do banco de dados
$menus = carregarMenus($pdo, $usuario_permissoes ?? ['admin']);

// Incluir o header
include_once 'includes/header.php';
?>

<div class="container">
    <div class="d-flex justify-content-between align-items-center mb-4">
        <div>
            <h1>Relatórios</h1>
            <p class="text-muted">Análise de pré-cadastros e métricas do sistema</p>
        </div>
        <div>
            <button type="button" class="btn btn-primary" id="btnImprimirRelatorio">
                <i class="fas fa-print"></i> Imprimir Relatório
            </button>
            <button type="button" class="btn btn-outline-secondary ms-2" id="btnExportarPDF">
                <i class="fas fa-file-pdf"></i> Exportar PDF
            </button>
        </div>
    </div>

    <?php if ($mensagem_erro): ?>
        <div class="alert alert-danger alert-dismissible fade show" role="alert">
            <?php echo htmlspecialchars($mensagem_erro); ?>
            <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
        </div>
    <?php endif; ?>
    
    <?php if ($mensagem_sucesso): ?>
        <div class="alert alert-success alert-dismissible fade show" role="alert">
            <?php echo htmlspecialchars($mensagem_sucesso); ?>
            <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
        </div>
    <?php endif; ?>

    <!-- Filtros do relatório -->
    <div class="card mb-4">
        <div class="card-header bg-light">
            <h5 class="mb-0"><i class="fas fa-filter me-2"></i>Filtros</h5>
        </div>
        <div class="card-body">
            <form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" class="row g-3">
                <input type="hidden" name="gerar_relatorio" value="1">
                <!-- ADICIONE CSRF TOKEN AQUI !!! -->
                
                <div class="col-md-4">
                    <label for="data_inicio" class="form-label">Data Inicial</label>
                    <input type="date" class="form-control" id="data_inicio" name="data_inicio"
                           value="<?php echo htmlspecialchars($filtro_data_inicio); ?>">
                </div>
                
                <div class="col-md-4">
                    <label for="data_fim" class="form-label">Data Final</label>
                    <input type="date" class="form-control" id="data_fim" name="data_fim"
                           value="<?php echo htmlspecialchars($filtro_data_fim); ?>">
                </div>
                
                <div class="col-md-4">
                    <label for="status" class="form-label">Status</label>
                    <select class="form-select" id="status" name="status">
                        <option value="">Todos os Status</option>
                        <?php foreach ($status_options as $value => $label): ?>
                            <option value="<?php echo htmlspecialchars($value); ?>" 
                                    <?php echo ($filtro_status === $value) ? 'selected' : ''; ?>>
                                <?php echo htmlspecialchars($label); ?>
                            </option>
                        <?php endforeach; ?>
                    </select>
                </div>
                
                <div class="col-12 mt-3">
                    <button type="submit" class="btn btn-primary">
                        <i class="fas fa-search me-2"></i>Gerar Relatório
                    </button>
                    <a href="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" class="btn btn-outline-secondary ms-2">
                        <i class="fas fa-redo me-2"></i>Limpar Filtros
                    </a>
                </div>
            </form>
        </div>
    </div>

    <!-- Resumo do relatório -->
    <div class="row mb-4">
        <div class="col-md-4 mb-3">
            <div class="card bg-primary text-white h-100">
                <div class="card-body text-center">
                    <h5 class="card-title">Total de Cadastros</h5>
                    <h2 class="display-4"><?php echo number_format($relatorio_data['total_geral'] ?? 0); ?></h2>
                    <p class="card-text">No período selecionado</p>
                </div>
            </div>
        </div>
        
        <div class="col-md-4 mb-3">
            <div class="card bg-warning text-dark h-100">
                <div class="card-body text-center">
                    <h5 class="card-title">Pendentes de Processamento</h5>
                    <h2 class="display-4"><?php echo number_format($relatorio_data['total_pendentes'] ?? 0); ?></h2>
                    <p class="card-text">Aguardando envio para API</p>
                </div>
            </div>
        </div>
        
        <div class="col-md-4 mb-3">
            <div class="card bg-success text-white h-100">
                <div class="card-body text-center">
                    <h5 class="card-title">Processados com Sucesso</h5>
                    <h2 class="display-4"><?php echo number_format($relatorio_data['total_processados'] ?? 0); ?></h2>
                    <p class="card-text">Enviados para API</p>
                </div>
            </div>
        </div>
    </div>

    <!-- Gráficos -->
    <div class="row mb-4">
        <div class="col-md-6 mb-4">
            <div class="card h-100">
                <div class="card-header bg-light">
                    <h5 class="mb-0"><i class="fas fa-chart-pie me-2"></i>Cadastros por Status</h5>
                </div>
                <div class="card-body">
                    <canvas id="chartStatus" width="400" height="300"></canvas>
                </div>
            </div>
        </div>
        
        <div class="col-md-6 mb-4">
            <div class="card h-100">
                <div class="card-header bg-light">
                    <h5 class="mb-0"><i class="fas fa-chart-line me-2"></i>Cadastros por Dia</h5>
                </div>
                <div class="card-body">
                    <canvas id="chartDiario" width="400" height="300"></canvas>
                </div>
            </div>
        </div>
    </div>

    <div class="row mb-4">
        <div class="col-md-6 mb-4">
            <div class="card h-100">
                <div class="card-header bg-light">
                    <h5 class="mb-0"><i class="fas fa-chart-bar me-2"></i>Top 5 Cidades</h5>
                </div>
                <div class="card-body">
                    <canvas id="chartCidades" width="400" height="300"></canvas>
                </div>
            </div>
        </div>
        
        <div class="col-md-6 mb-4">
            <div class="card h-100">
                <div class="card-header bg-light">
                    <h5 class="mb-0"><i class="fas fa-table me-2"></i>Dados por Status</h5>
                </div>
                <div class="card-body">
                    <div class="table-responsive">
                        <table class="table table-striped table-hover">
                            <thead>
                                <tr>
                                    <th>Status</th>
                                    <th class="text-center">Quantidade</th>
                                    <th class="text-center">Percentual</th>
                                </tr>
                            </thead>
                            <tbody>
                                <?php 
                                $total_geral = $relatorio_data['total_geral'] ?? 0;
                                if (!empty($relatorio_data['status'])):
                                    foreach ($relatorio_data['status'] as $status_item):
                                        $percentual = ($total_geral > 0) ? ($status_item['total'] / $total_geral) * 100 : 0;
                                ?>
                                <tr>
                                    <td>
                                        <span class="badge status-<?php echo htmlspecialchars($status_item['status_processamento']); ?>">
                                            <?php echo ucwords(str_replace('_', ' ', htmlspecialchars($status_item['status_processamento']))); ?>
                                        </span>
                                    </td>
                                    <td class="text-center"><?php echo number_format($status_item['total']); ?></td>
                                    <td class="text-center"><?php echo number_format($percentual, 1); ?>%</td>
                                </tr>
                                <?php 
                                    endforeach;
                                else:
                                ?>
                                <tr>
                                    <td colspan="3" class="text-center">Nenhum dado encontrado.</td>
                                </tr>
                                <?php endif; ?>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<!-- Carregamento do Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.1/dist/chart.min.js"></script>

<script>
    // Dados dos gráficos
    const statusLabels = <?php echo json_encode($graficos_data['status_labels'] ?? []); ?>;
    const statusValues = <?php echo json_encode($graficos_data['status_values'] ?? []); ?>;
    const statusColors = <?php echo json_encode($graficos_data['status_colors'] ?? []); ?>;
    
    const diarioLabels = <?php echo json_encode($graficos_data['diario_labels'] ?? []); ?>;
    const diarioValues = <?php echo json_encode($graficos_data['diario_values'] ?? []); ?>;
    
    const cidadesLabels = <?php echo json_encode($graficos_data['cidades_labels'] ?? []); ?>;
    const cidadesValues = <?php echo json_encode($graficos_data['cidades_values'] ?? []); ?>;
    
    // Configuração dos gráficos
    document.addEventListener('DOMContentLoaded', function() {
        // Gráfico de Status (Pie Chart)
        if (document.getElementById('chartStatus')) {
            const ctxStatus = document.getElementById('chartStatus').getContext('2d');
            const chartStatus = new Chart(ctxStatus, {
                type: 'pie',
                data: {
                    labels: statusLabels,
                    datasets: [{
                        data: statusValues,
                        backgroundColor: statusColors,
                        borderWidth: 1
                    }]
                },
                options: {
                    responsive: true,
                    plugins: {
                        legend: {
                            position: 'right',
                        },
                        tooltip: {
                            callbacks: {
                                label: function(context) {
                                    const label = context.label || '';
                                    const value = context.raw || 0;
                                    const total = context.dataset.data.reduce((a, b) => a + b, 0);
                                    const percentage = total > 0 ? Math.round((value / total) * 100) : 0;
                                    return `${label}: ${value} (${percentage}%)`;
                                }
                            }
                        }
                    }
                }
            });
        }
        
        // Gráfico Diário (Line Chart)
        if (document.getElementById('chartDiario')) {
            const ctxDiario = document.getElementById('chartDiario').getContext('2d');
            const chartDiario = new Chart(ctxDiario, {
                type: 'line',
                data: {
                    labels: diarioLabels,
                    datasets: [{
                        label: 'Cadastros por Dia',
                        data: diarioValues,
                        fill: false,
                        borderColor: 'rgb(75, 192, 192)',
                        tension: 0.1
                    }]
                },
                options: {
                    responsive: true,
                    plugins: {
                        legend: {
                            display: false
                        }
                    },
                    scales: {
                        y: {
                            beginAtZero: true,
                            ticks: {
                                precision: 0
                            }
                        }
                    }
                }
            });
        }
        
        // Gráfico de Cidades (Bar Chart)
        if (document.getElementById('chartCidades')) {
            const ctxCidades = document.getElementById('chartCidades').getContext('2d');
            const chartCidades = new Chart(ctxCidades, {
                type: 'bar',
                data: {
                    labels: cidadesLabels,
                    datasets: [{
                        label: 'Cadastros por Cidade',
                        data: cidadesValues,
                        backgroundColor: [
                            'rgba(255, 99, 132, 0.7)',
                            'rgba(54, 162, 235, 0.7)',
                            'rgba(255, 206, 86, 0.7)',
                            'rgba(75, 192, 192, 0.7)',
                            'rgba(153, 102, 255, 0.7)'
                        ],
                        borderColor: [
                            'rgba(255, 99, 132, 1)',
                            'rgba(54, 162, 235, 1)',
                            'rgba(255, 206, 86, 1)',
                            'rgba(75, 192, 192, 1)',
                            'rgba(153, 102, 255, 1)'
                        ],
                        borderWidth: 1
                    }]
                },
                options: {
                    responsive: true,
                    plugins: {
                        legend: {
                            display: false
                        }
                    },
                    scales: {
                        y: {
                            beginAtZero: true,
                            ticks: {
                                precision: 0
                            }
                        }
                    }
                }
            });
        }
    });
    
    // Função para impressão de relatório
    document.getElementById('btnImprimirRelatorio').addEventListener('click', function() {
        window.print();
    });
    
    // Função para exportar PDF (simulada)
    document.getElementById('btnExportarPDF').addEventListener('click', function() {
        alert('Esta função exportaria o relatório para PDF.\nPara implementação real, você precisaria de uma biblioteca como jsPDF ou enviar o pedido para o servidor gerar o PDF.');
    });
</script>

<?php
// Incluir o footer
include_once 'includes/footer.php';
?>