Files
acaoopcao/cotacoes.php
T
2026-08-21 22:57:44 +00:00

238 lines
11 KiB
PHP

<?php
$titulo_pagina = 'Cotações';
$pagina_ativa = 'cotacoes';
require_once __DIR__ . '/includes/config.php';
require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/includes/auth.php';
ao_exigir_permissao('cotacoes');
$ao_usuario_id = (int) ao_usuario_logado()['id'];
/** Converte "38,42" ou "38.42" em float. Retorna null se não for um número válido. */
function ao_cot_parse_decimal_br($valor) {
$valor = str_replace(',', '.', trim((string) $valor));
return is_numeric($valor) ? (float) $valor : null;
}
$erro = null;
$sucesso = null;
// Passa a acompanhar um novo ativo. A cotação em si mora numa tabela única e
// global ("cotacoes"): se o ativo ainda não existe ali, criamos com preço
// zerado (até alguém informar o primeiro valor); se já existe, é só ligar
// este usuário a ele.
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['acao'] ?? '') === 'acompanhar') {
$ativo = strtoupper(trim($_POST['ativo'] ?? ''));
if ($ativo === '') {
$erro = 'Informe o código do ativo que você quer acompanhar.';
} else {
try {
$pdo->prepare('INSERT IGNORE INTO cotacoes (ativo) VALUES (?)')->execute([$ativo]);
$pdo->prepare('INSERT INTO cotacoes_acompanhamentos (usuario_id, ativo) VALUES (?, ?)')
->execute([$ao_usuario_id, $ativo]);
header('Location: cotacoes.php?acompanhado=1');
exit;
} catch (PDOException $e) {
$erro = ($e->getCode() === '23000') ? ('Você já acompanha ' . $ativo . '.') : ('Erro ao salvar: ' . $e->getMessage());
}
}
}
// Para de acompanhar (não mexe na cotação compartilhada, só na lista pessoal).
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['acao'] ?? '') === 'parar') {
$id = (int) ($_POST['id'] ?? 0);
$pdo->prepare('DELETE FROM cotacoes_acompanhamentos WHERE id = ? AND usuario_id = ?')->execute([$id, $ao_usuario_id]);
header('Location: cotacoes.php?parado=1');
exit;
}
// Atualiza o preço de um ativo na tabela única/global — vale para todo mundo
// que acompanha esse ativo, não só para quem atualizou. Só deixamos atualizar
// o preço de um ativo que o próprio usuário acompanha (evita alguém ficar
// alterando cotação de ativo que nem apareceu pra ele na tela).
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['acao'] ?? '') === 'atualizar_preco') {
$ativo = strtoupper(trim($_POST['ativo'] ?? ''));
$preco_bruto = trim($_POST['preco'] ?? '');
$preco = ao_cot_parse_decimal_br($preco_bruto);
$stmtAcompanha = $pdo->prepare('SELECT 1 FROM cotacoes_acompanhamentos WHERE usuario_id = ? AND ativo = ?');
$stmtAcompanha->execute([$ao_usuario_id, $ativo]);
if (!$stmtAcompanha->fetch()) {
$erro = 'Você só pode atualizar o preço de ativos que você acompanha.';
} elseif ($preco === null || $preco <= 0) {
$erro = 'Informe um preço válido (maior que zero).';
} else {
$stmtAtual = $pdo->prepare('SELECT preco_atual FROM cotacoes WHERE ativo = ?');
$stmtAtual->execute([$ativo]);
$preco_atual_antigo = (float) $stmtAtual->fetchColumn();
// Se é a primeira vez que alguém informa um preço para este ativo, não
// dá pra calcular variação a partir de zero — começa "zerada" (0%) em
// vez de mostrar um salto absurdo de +infinito.
$preco_anterior = $preco_atual_antigo > 0 ? $preco_atual_antigo : $preco;
$stmt = $pdo->prepare('UPDATE cotacoes SET preco_anterior = ?, preco_atual = ?, atualizado_por = ? WHERE ativo = ?');
$stmt->execute([$preco_anterior, $preco, $ao_usuario_id, $ativo]);
header('Location: cotacoes.php?atualizado=1');
exit;
}
}
if (isset($_GET['acompanhado'])) $sucesso = 'Ativo adicionado à sua lista de acompanhamento.';
if (isset($_GET['parado'])) $sucesso = 'Você parou de acompanhar esse ativo.';
if (isset($_GET['atualizado'])) $sucesso = 'Cotação atualizada — todo mundo que acompanha esse ativo já vê o novo preço.';
// Mantém o que foi digitado no formulário de "acompanhar" se deu erro.
$ativo_digitado = ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['acao'] ?? '') === 'acompanhar')
? (string) ($_POST['ativo'] ?? '')
: '';
// A lista de preços vem sempre da tabela "cotacoes" (única/global) — aqui só
// filtramos para os ativos que ESTE usuário decidiu acompanhar.
$stmt = $pdo->prepare(
'SELECT ca.id AS acompanhamento_id, c.ativo, c.preco_atual, c.preco_anterior, c.atualizado_em, c.atualizado_por,
u.nome AS atualizado_por_nome
FROM cotacoes_acompanhamentos ca
INNER JOIN cotacoes c ON c.ativo = ca.ativo
LEFT JOIN usuarios u ON u.id = c.atualizado_por
WHERE ca.usuario_id = ?
ORDER BY c.ativo'
);
$stmt->execute([$ao_usuario_id]);
$acompanhados = $stmt->fetchAll();
// Calcula a variação percentual de cada um e separa em altas (>= 0%) e
// baixas (< 0%) para as duas grids pedidas.
foreach ($acompanhados as &$a) {
$anterior = (float) $a['preco_anterior'];
$atual = (float) $a['preco_atual'];
$a['tem_cotacao'] = $a['atualizado_por'] !== null;
$a['variacao'] = $anterior > 0 ? (($atual - $anterior) / $anterior) * 100 : 0.0;
}
unset($a);
$altas = array_values(array_filter($acompanhados, fn($a) => $a['variacao'] >= 0));
$baixas = array_values(array_filter($acompanhados, fn($a) => $a['variacao'] < 0));
usort($altas, fn($a, $b) => $b['variacao'] <=> $a['variacao']);
usort($baixas, fn($a, $b) => $a['variacao'] <=> $b['variacao']);
require __DIR__ . '/includes/header.php';
/** Uma linha da grid de cotações (compartilhada entre a grid de altas e a de baixas). */
function ao_cot_linha(array $a) {
$classe = $a['variacao'] >= 0 ? 'ao-badge-alta' : 'ao-badge-baixa';
$seta = $a['variacao'] >= 0 ? '▲' : '▼';
?>
<tr>
<td class="ao-numeric"><?= htmlspecialchars($a['ativo']) ?></td>
<td class="ao-numeric">
<?php if ($a['tem_cotacao']): ?>
R$ <?= number_format((float) $a['preco_atual'], 2, ',', '.') ?>
<?php else: ?>
<span style="color: var(--ao-text-muted);">sem cotação ainda</span>
<?php endif; ?>
</td>
<td class="ao-numeric">
<?php if ($a['tem_cotacao']): ?>
<span class="ao-badge <?= $classe ?>"><?= $seta ?> <?= number_format(abs($a['variacao']), 2, ',', '.') ?>%</span>
<?php else: ?>
<span style="color: var(--ao-text-muted);">—</span>
<?php endif; ?>
</td>
<td style="color: var(--ao-text-muted); font-size: var(--ao-fs-xs);">
<?php if ($a['tem_cotacao']): ?>
<?= htmlspecialchars($a['atualizado_por_nome'] ?? '—') ?> em <?= htmlspecialchars(date('d/m/Y H:i', strtotime($a['atualizado_em']))) ?>
<?php else: ?>
<?php endif; ?>
</td>
<td style="white-space: nowrap; text-align: right;">
<form method="post" action="cotacoes.php" style="display:inline-flex; gap: 4px; align-items: center;">
<input type="hidden" name="acao" value="atualizar_preco">
<input type="hidden" name="ativo" value="<?= htmlspecialchars($a['ativo']) ?>">
<input type="text" name="preco" placeholder="Novo preço" required
style="width: 100px; padding: 4px 8px; font-size: var(--ao-fs-xs);">
<button type="submit" class="ao-btn ao-btn-secondary" style="padding: 4px 10px; font-size: var(--ao-fs-xs);">Atualizar</button>
</form>
<form method="post" action="cotacoes.php" style="display:inline;" onsubmit="return confirm('Parar de acompanhar <?= htmlspecialchars($a['ativo']) ?>?');">
<input type="hidden" name="acao" value="parar">
<input type="hidden" name="id" value="<?= (int) $a['acompanhamento_id'] ?>">
<button type="submit" class="ao-btn ao-btn-secondary" style="padding: 4px 10px; font-size: var(--ao-fs-xs);">Parar</button>
</form>
</td>
</tr>
<?php
}
?>
<h1 class="ao-page-title">Cotações do dia</h1>
<?php if ($erro): ?>
<div class="ao-badge ao-badge-baixa" style="display:block; margin-bottom: var(--ao-space-4); padding: 10px 14px; font-size: var(--ao-fs-sm);"><?= htmlspecialchars($erro) ?></div>
<?php endif; ?>
<?php if ($sucesso): ?>
<div class="ao-badge ao-badge-alta" style="display:block; margin-bottom: var(--ao-space-4); padding: 10px 14px; font-size: var(--ao-fs-sm);"><?= htmlspecialchars($sucesso) ?></div>
<?php endif; ?>
<div class="ao-card" style="max-width: 480px; margin-bottom: var(--ao-space-6);">
<div class="ao-card-header"><strong>Acompanhar novo ativo</strong></div>
<p style="color: var(--ao-text-muted); font-size: var(--ao-fs-sm); margin-top: 0;">
A cotação é compartilhada: qualquer pessoa que também acompanhe o ativo pode
atualizar o preço, e todo mundo que o acompanha vê o valor mais recente.
</p>
<form method="post" action="cotacoes.php" class="ao-row" style="gap: var(--ao-space-3); align-items: flex-end;">
<input type="hidden" name="acao" value="acompanhar">
<div class="ao-field" style="flex: 1; margin-bottom: 0;">
<label for="ativo">Código do ativo</label>
<input type="text" id="ativo" name="ativo" placeholder="Ex: PETR4" required
style="text-transform: uppercase;" value="<?= htmlspecialchars($ativo_digitado) ?>">
</div>
<button type="submit" class="ao-btn ao-btn-primary">Acompanhar</button>
</form>
</div>
<?php if (!$acompanhados): ?>
<div class="ao-card">
<p style="margin: 0; color: var(--ao-text-muted);">Você ainda não está acompanhando nenhum ativo. Use o formulário acima para adicionar o primeiro.</p>
</div>
<?php else: ?>
<div class="ao-card" style="margin-bottom: var(--ao-space-6);">
<div class="ao-card-header"><strong>Altas do dia</strong></div>
<table class="ao-table">
<thead>
<tr><th>Ativo</th><th>Último</th><th>Variação</th><th>Última atualização</th><th></th></tr>
</thead>
<tbody>
<?php if (!$altas): ?>
<tr><td colspan="5" style="color: var(--ao-text-muted);">Nenhum ativo em alta no momento.</td></tr>
<?php endif; ?>
<?php foreach ($altas as $a) ao_cot_linha($a); ?>
</tbody>
</table>
</div>
<div class="ao-card">
<div class="ao-card-header"><strong>Baixas do dia</strong></div>
<table class="ao-table">
<thead>
<tr><th>Ativo</th><th>Último</th><th>Variação</th><th>Última atualização</th><th></th></tr>
</thead>
<tbody>
<?php if (!$baixas): ?>
<tr><td colspan="5" style="color: var(--ao-text-muted);">Nenhum ativo em baixa no momento.</td></tr>
<?php endif; ?>
<?php foreach ($baixas as $a) ao_cot_linha($a); ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php require __DIR__ . '/includes/footer.php'; ?>