File: /home/onlyfibr/public_html/cadastro/admin/cadastros_pendentes.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 = 'Cadastros Pendentes';
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 Ações (POST Requests) ---
$mensagem_erro = $_SESSION['erro_dashboard'] ?? null;
$mensagem_sucesso = $_SESSION['sucesso_dashboard'] ?? null;
$erro_busca_db = ''; // Inicializa como string vazia
unset($_SESSION['erro_dashboard'], $_SESSION['sucesso_dashboard']);
// Geração de token CSRF
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$csrf_token = $_SESSION['csrf_token'];
// --- Ação: Processar API Manualmente ---
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'process_api') {
// Verificação do token CSRF
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
$_SESSION['erro_dashboard'] = "Erro de validação do formulário. Por favor, tente novamente.";
header('Location: ' . htmlspecialchars($_SERVER['PHP_SELF']));
exit;
}
$id_para_processar = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT);
if ($id_para_processar && $pdo) {
try {
// 1. Buscar todos os dados necessários do pré-cadastro
// Certifique-se de incluir TODOS os campos que a API espera.
$sqlFetch = "SELECT * FROM precadastros WHERE id = :id AND p_processar = TRUE LIMIT 1";
$stmtFetch = $pdo->prepare($sqlFetch);
$stmtFetch->bindParam(':id', $id_para_processar, PDO::PARAM_INT);
$stmtFetch->execute();
$data = $stmtFetch->fetch(PDO::FETCH_ASSOC);
if ($data) {
// 2. Determinar o endpoint correto
$endpointApi = ($data['tipo_pessoa'] === 'PF') ? '/api/precadastro/F' : '/api/precadastro/J';
// 3. Montar o Payload da API (similar ao assinar.php)
// Adapte os nomes dos campos conforme a API espera e os dados do seu DB.
$payloadApi = [
'app' => APP_NAME,
'token' => APP_TOKEN,
'nome' => $data['nome'],
'cpfcnpj' => $data['cpf_cnpj'],
'email' => $data['email'],
'celular' => $data['celular'],
'logradouro' => $data['logradouro'],
'numero' => (int)$data['numero'], // Garante que seja inteiro
'complemento' => $data['complemento'] ?: null,
'bairro' => $data['bairro'],
'cidade' => $data['cidade'],
'cep' => $data['cep'], // Certifique-se que está sem máscara
'uf' => $data['estado'], // Ajuste se o nome da coluna for diferente
'pais' => 'BR', // Ou buscar do DB se necessário
'pontoreferencia' => $data['ponto_referencia'] ?: null, // Ajuste nome da coluna
'observacao' => $data['observacao'] ?: null,
'plano_id' => $data['plano_id_selecionado'], // Ajuste nome da coluna
'vencimento_id' => $data['vencimento_id_selecionado'], // Ajuste nome da coluna
'vendedor_id' => $data['vendedor_id'] ?: null, // Ajuste nome da coluna
// Campos específicos PF/PJ
// Adicione aqui os campos RG, Data Nasc, Nome Fantasia, IE, etc., se existirem na sua tabela e forem necessários pela API
];
if ($data['tipo_pessoa'] === 'PF') {
$payloadApi['rg'] = $data['rg'] ?? null; // Ajuste nome da coluna
$payloadApi['rg_emissor'] = $data['rg_emissor'] ?? null; // Ajuste nome da coluna
$payloadApi['datanasc'] = $data['data_nascimento'] ?? null; // Ajuste nome da coluna e formato se necessário
}
if ($data['tipo_pessoa'] === 'PJ') {
$payloadApi['nomefantasia'] = $data['nome_fantasia'] ?? null; // Ajuste nome da coluna
$payloadApi['insc_estadual'] = $data['insc_estadual'] ?? null; // Ajuste nome da coluna
}
// Remover chaves com valor null, se a API não os aceitar
$payloadApi = array_filter($payloadApi, function ($value) {
return $value !== null;
});
// 4. Chamar a API
$responseApi = postApiJson(BASE_URL, $endpointApi, $payloadApi);
$resultadoApi = json_decode($responseApi, true);
$api_response_msg = $responseApi; // Salva a resposta crua
// 5. Verificar o resultado e atualizar o banco
// A condição de sucesso pode variar dependendo da resposta da sua API
if (json_last_error() === JSON_ERROR_NONE && isset($resultadoApi['message']) && stripos($resultadoApi['message'], 'criado com sucesso') !== false) {
// Sucesso! Atualiza p_processar para FALSE e status_processamento
$sqlUpdate = "UPDATE precadastros SET p_processar = :p_proc, status_processamento = 'aprovado', resposta_api = :resp WHERE id = :id";
$stmtUpdate = $pdo->prepare($sqlUpdate);
// Define explicitamente o valor como booleano e usa PDO::PARAM_BOOL
$stmtUpdate->bindValue(':p_proc', false, PDO::PARAM_BOOL);
$stmtUpdate->bindValue(':resp', "Processado manualmente com sucesso: " . $api_response_msg);
$stmtUpdate->bindParam(':id', $id_para_processar, PDO::PARAM_INT);
$stmtUpdate->execute(); // Esta linha agora usa o valor booleano corretamente vinculado
$_SESSION['sucesso_dashboard'] = "Registro ID {$id_para_processar} processado e enviado para a API com sucesso.";
} else {
// Falha na API
$erro_api_msg = $resultadoApi['message'] ?? ($resultadoApi['msg'] ?? 'Erro desconhecido retornado pela API.');
// Atualiza apenas a resposta da API, mas mantém p_processar = TRUE
$sqlUpdate = "UPDATE precadastros SET resposta_api = :resp WHERE id = :id";
$stmtUpdate = $pdo->prepare($sqlUpdate);
$stmtUpdate->bindValue(':resp', "Falha no processamento manual: " . $api_response_msg);
$stmtUpdate->bindParam(':id', $id_para_processar, PDO::PARAM_INT);
$stmtUpdate->execute();
$_SESSION['erro_dashboard'] = "Erro ao processar API para ID {$id_para_processar}: " . htmlspecialchars($erro_api_msg);
error_log("Erro API processamento manual $id_para_processar ($endpointApi) (PGSQL). Payload: " . json_encode($payloadApi) . ". Resp: " . $responseApi);
}
} else {
$_SESSION['erro_dashboard'] = "Registro ID {$id_para_processar} não encontrado ou já processado.";
}
} catch (\PDOException $e) {
$failing_sql = $stmtUpdate->queryString ?? ($stmtFetch->queryString ?? 'Query Desconhecida');
error_log("PDOException ao processar API ID {$id_para_processar}. SQLState: " . $e->getCode() . ". Mensagem PDO: " . $e->getMessage() . ". Query Provável: " . $failing_sql);
$_SESSION['erro_dashboard'] = "Erro de banco de dados [Ref:" . $e->getCode() . "] ao atualizar registro. Verifique os logs.";
} catch (\Exception $e) { // Captura erros gerais (ex: falha na função postApiJson)
error_log("Erro GERAL ao processar API manualmente para ID {$id_para_processar}: " . $e->getMessage());
$_SESSION['erro_dashboard'] = "Erro inesperado ao processar API para ID {$id_para_processar}. Verifique os logs.";
}
} else {
$_SESSION['erro_dashboard'] = "ID inválido para processamento.";
}
// Redireciona para limpar POST e atualizar a tabela
header('Location: ' . htmlspecialchars($_SERVER['PHP_SELF']));
exit;
}
// --- Ação: Processar Todos os Registros Pendentes ---
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'process_all') {
// Verificação do token CSRF
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
$_SESSION['erro_dashboard'] = "Erro de validação do formulário. Por favor, tente novamente.";
header('Location: ' . htmlspecialchars($_SERVER['PHP_SELF']));
exit;
}
$registros_processados = 0;
$registros_com_erro = 0;
if ($pdo) {
try {
// 1. Buscar todos os registros pendentes
$sqlFetchAll = "SELECT id FROM precadastros WHERE p_processar = TRUE ORDER BY id";
$stmtFetchAll = $pdo->query($sqlFetchAll);
$pendentes = $stmtFetchAll->fetchAll(PDO::FETCH_COLUMN);
if (!empty($pendentes)) {
foreach ($pendentes as $id_registro) {
// Para cada registro pendente, executamos o processamento individual
try {
// Buscar dados do registro
$sqlFetch = "SELECT * FROM precadastros WHERE id = :id AND p_processar = TRUE LIMIT 1";
$stmtFetch = $pdo->prepare($sqlFetch);
$stmtFetch->bindParam(':id', $id_registro, PDO::PARAM_INT);
$stmtFetch->execute();
$data = $stmtFetch->fetch(PDO::FETCH_ASSOC);
if (!$data) {
$registros_com_erro++;
continue; // Pula para o próximo registro
}
// Determinar o endpoint correto
$endpointApi = ($data['tipo_pessoa'] === 'PF') ? '/api/precadastro/F' : '/api/precadastro/J';
// Construir o payload
$payloadApi = [
'app' => APP_NAME,
'token' => APP_TOKEN,
'nome' => $data['nome'],
'cpfcnpj' => $data['cpf_cnpj'],
'email' => $data['email'],
'celular' => $data['celular'],
'logradouro' => $data['logradouro'],
'numero' => (int)$data['numero'],
'complemento' => $data['complemento'] ?: null,
'bairro' => $data['bairro'],
'cidade' => $data['cidade'],
'cep' => $data['cep'],
'uf' => $data['estado'],
'pais' => 'BR',
'pontoreferencia' => $data['ponto_referencia'] ?: null,
'observacao' => $data['observacao'] ?: null,
'plano_id' => $data['plano_id_selecionado'],
'vencimento_id' => $data['vencimento_id_selecionado'],
'vendedor_id' => $data['vendedor_id'] ?: null,
];
// Adicionar campos específicos para PF ou PJ
if ($data['tipo_pessoa'] === 'PF') {
$payloadApi['rg'] = $data['rg'] ?? null;
$payloadApi['rg_emissor'] = $data['rg_emissor'] ?? null;
$payloadApi['datanasc'] = $data['data_nascimento'] ?? null;
}
if ($data['tipo_pessoa'] === 'PJ') {
$payloadApi['nomefantasia'] = $data['nome_fantasia'] ?? null;
$payloadApi['insc_estadual'] = $data['insc_estadual'] ?? null;
}
// Remover valores nulos
$payloadApi = array_filter($payloadApi, function ($value) {
return $value !== null;
});
// Chamar API
$responseApi = postApiJson(BASE_URL, $endpointApi, $payloadApi);
$resultadoApi = json_decode($responseApi, true);
// Verificar resultado e atualizar banco
if (json_last_error() === JSON_ERROR_NONE && isset($resultadoApi['message']) && stripos($resultadoApi['message'], 'criado com sucesso') !== false) {
// Sucesso
$sqlUpdate = "UPDATE precadastros SET p_processar = :p_proc, status_processamento = 'aprovado', resposta_api = :resp WHERE id = :id";
$stmtUpdate = $pdo->prepare($sqlUpdate);
$stmtUpdate->bindValue(':p_proc', false, PDO::PARAM_BOOL);
$stmtUpdate->bindValue(':resp', "Processado em lote com sucesso: " . $responseApi);
$stmtUpdate->bindParam(':id', $id_registro, PDO::PARAM_INT);
$stmtUpdate->execute();
$registros_processados++;
} else {
// Falha
$erro_api_msg = $resultadoApi['message'] ?? ($resultadoApi['msg'] ?? 'Erro desconhecido retornado pela API.');
$sqlUpdate = "UPDATE precadastros SET resposta_api = :resp WHERE id = :id";
$stmtUpdate = $pdo->prepare($sqlUpdate);
$stmtUpdate->bindValue(':resp', "Falha no processamento em lote: " . $responseApi);
$stmtUpdate->bindParam(':id', $id_registro, PDO::PARAM_INT);
$stmtUpdate->execute();
$registros_com_erro++;
error_log("Erro API processamento em lote $id_registro ($endpointApi). Payload: " . json_encode($payloadApi) . ". Resp: " . $responseApi);
}
} catch (\Exception $e) {
$registros_com_erro++;
error_log("Erro ao processar ID $id_registro em lote: " . $e->getMessage());
}
}
// Feedback final
if ($registros_processados > 0) {
$_SESSION['sucesso_dashboard'] = "Processamento em lote concluído. $registros_processados registros processados com sucesso" .
($registros_com_erro > 0 ? " e $registros_com_erro com erro." : ".");
} else {
$_SESSION['erro_dashboard'] = "Nenhum registro processado com sucesso. $registros_com_erro registros com erro.";
}
} else {
$_SESSION['mensagem_dashboard'] = "Não foram encontrados registros pendentes de processamento.";
}
} catch (\PDOException $e) {
error_log("Erro DB ao processar todos os registros: " . $e->getMessage());
$_SESSION['erro_dashboard'] = "Erro de banco de dados ao processar registros em lote. Verifique os logs.";
} catch (\Exception $e) {
error_log("Erro geral ao processar todos os registros: " . $e->getMessage());
$_SESSION['erro_dashboard'] = "Erro inesperado ao processar registros em lote. Verifique os logs.";
}
}
// Redireciona para limpar POST e atualizar a tabela
header('Location: ' . htmlspecialchars($_SERVER['PHP_SELF']));
exit;
}
// --- Busca dos Dados para Listagem ---
$lista_pendentes = [];
$erro_busca_db = '';
if ($pdo) {
try {
// Busca apenas os registros pendentes (p_processar = TRUE)
$sql = "SELECT id, nome, email, celular, cpf_cnpj, cidade, estado, tipo_moradia, status_processamento, data_envio, resposta_api, tipo_pessoa
FROM precadastros
WHERE p_processar = TRUE
ORDER BY data_envio DESC, id DESC"; // Ordena pelos mais recentes
$stmt = $pdo->query($sql);
$lista_pendentes = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (\PDOException $e) {
$erro_busca_db = "Erro ao buscar dados: " . $e->getMessage();
error_log("Erro DB Pendentes: " . $e->getMessage());
}
} else {
$erro_busca_db = "Erro crítico: Conexão com banco de dados falhou.";
}
// Contador total de pendentes
$total_pendentes = count($lista_pendentes);
// 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-3">
<div>
<h1>Cadastros Pendentes de Processamento</h1>
<p class="text-muted">Registros que precisam ser enviados para a API SGP</p>
</div>
<div>
<?php if ($total_pendentes > 0): ?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" class="d-inline" id="processarTodosForm">
<input type="hidden" name="action" value="process_all">
<input type="hidden" name="csrf_token" value="<?php echo $csrf_token; ?>">
<button type="submit" class="btn btn-warning" id="btnProcessarTodos">
<i class="fas fa-paper-plane"></i> Processar Todos (<?php echo $total_pendentes; ?>)
</button>
</form>
<?php endif; ?>
<a href="listar_precadastros.php" class="btn btn-outline-secondary ms-2">
<i class="fas fa-list"></i> Ver Todos os Cadastros
</a>
</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; ?>
<?php if ($erro_busca_db): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($erro_busca_db); ?></div>
<?php endif; ?>
<?php if (empty($lista_pendentes)): ?>
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i> Não há cadastros pendentes de processamento no momento.
</div>
<?php else: ?>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Nome</th>
<th>E-mail/WhatsApp</th>
<th>CPF/CNPJ</th>
<th>Cidade/UF</th>
<th>Status</th>
<th>Data/Hora</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
<?php foreach ($lista_pendentes as $cadastro): ?>
<tr>
<td><?php echo htmlspecialchars($cadastro['id']); ?></td>
<td><?php echo htmlspecialchars($cadastro['nome']); ?></td>
<td>
<small>
<?php echo htmlspecialchars($cadastro['email']); ?><br>
<?php echo htmlspecialchars($cadastro['celular']); ?>
</small>
</td>
<td><?php echo htmlspecialchars($cadastro['cpf_cnpj']); ?></td>
<td><?php echo htmlspecialchars($cadastro['cidade'] . '/' . $cadastro['estado']); ?></td>
<td>
<span class="badge status-<?php echo htmlspecialchars($cadastro['status_processamento']); ?>">
<?php echo ucwords(str_replace('_', ' ', htmlspecialchars($cadastro['status_processamento']))); ?>
</span>
</td>
<td>
<small><?php echo date('d/m/Y H:i', strtotime($cadastro['data_envio'])); ?></small>
</td>
<td>
<div class="btn-group">
<form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>">
<input type="hidden" name="action" value="process_api">
<input type="hidden" name="id" value="<?php echo $cadastro['id']; ?>">
<input type="hidden" name="csrf_token" value="<?php echo $csrf_token; ?>">
<button type="submit" class="btn btn-sm btn-primary" title="Processar API">
<i class="fas fa-paper-plane"></i>
</button>
</form>
<a href="editar_precadastro.php?id=<?php echo $cadastro['id']; ?>" class="btn btn-sm btn-info ms-1" title="Editar">
<i class="fas fa-edit"></i>
</a>
<a href="ver_precadastro.php?id=<?php echo $cadastro['id']; ?>" class="btn btn-sm btn-secondary ms-1" title="Visualizar Detalhes">
<i class="fas fa-eye"></i>
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Confirmação para processar todos os registros
const formProcessarTodos = document.getElementById('processarTodosForm');
if (formProcessarTodos) {
formProcessarTodos.addEventListener('submit', function(e) {
if (!confirm('Tem certeza que deseja processar todos os <?php echo $total_pendentes; ?> registros pendentes?')) {
e.preventDefault();
}
});
}
// Adicionar tooltips do Bootstrap se necessário
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[title]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
});
});
</script>
<!-- Estilos para os badges de status -->
<style>
.status-pendente {
background-color: #ffc107;
color: #212529;
}
.status-aprovado {
background-color: #198754;
}
.status-rejeitado {
background-color: #dc3545;
}
.status-processando {
background-color: #0dcaf0;
color: #212529;
}
.status-erro {
background-color: #6c757d;
}
</style>
<?php
// Incluir o footer
include_once 'includes/footer.php';
?>