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

496 lines
24 KiB
PHP

<?php
$titulo_pagina = 'Ações';
$pagina_ativa = 'acoes';
require_once __DIR__ . '/includes/config.php';
require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/includes/auth.php';
ao_exigir_permissao('acoes');
// Tudo nesta página é isolado por usuário: cada um só enxerga e só consegue
// alterar as próprias operações. Todo acesso por id abaixo é sempre filtrado
// também por usuario_id, para que ninguém edite/exclua/veja dados de outra
// pessoa forjando o id na URL ou no formulário.
$ao_usuario_id = (int) ao_usuario_logado()['id'];
// Categoria do ativo (Ação/ETF/BDR/FII) — é uma característica do próprio
// ativo, não da operação, então todas as operações de um mesmo código
// (ex: PETR4) precisam usar a mesma categoria (checado na validação do save).
const AO_CATEGORIAS_ATIVO = [
'acao' => 'Ação',
'etf' => 'ETF',
'bdr' => 'BDR',
'fii' => 'FII',
];
/**
* Simula, em ordem cronológica, uma lista de operações (compra/venda) de um
* mesmo ativo e devolve o estado final. Regra do PM (preço médio):
* - compra: recalcula a média ponderada e aumenta a quantidade.
* - venda: só reduz a quantidade; o PM das ações que sobram não muda.
* "ok" vem false se em algum ponto a quantidade ficaria negativa (ou seja,
* uma venda maior do que o que havia disponível até aquela data).
* Cada item de $linhas precisa ter: tipo, quantidade, preco, data_operacao, _ordem.
*/
function ao_simular_transacoes(array $linhas) {
usort($linhas, function ($a, $b) {
$an = $a['data_operacao'] === null;
$bn = $b['data_operacao'] === null;
if ($an !== $bn) {
return $an <=> $bn; // datadas primeiro, sem data por último
}
if (!$an && $a['data_operacao'] !== $b['data_operacao']) {
return strcmp($a['data_operacao'], $b['data_operacao']);
}
return $a['_ordem'] <=> $b['_ordem'];
});
$qtd = 0;
$pm = 0.0;
$ok = true;
foreach ($linhas as $l) {
$q = (int) $l['quantidade'];
$p = (float) $l['preco'];
if ($l['tipo'] === 'compra') {
$novoQtd = $qtd + $q;
$pm = $novoQtd > 0 ? (($qtd * $pm) + ($q * $p)) / $novoQtd : 0.0;
$qtd = $novoQtd;
} else {
$qtd -= $q;
if ($qtd < 0) {
$ok = false;
}
}
}
return ['ok' => $ok, 'qtd' => max(0, $qtd), 'pm' => round($pm, 4)];
}
/** Busca as operações de um ativo (de um usuário), no formato usado por ao_simular_transacoes(). */
function ao_buscar_transacoes_ativo(PDO $pdo, $usuario_id, $ativo, $ignorar_id = null) {
$sql = 'SELECT id, tipo, quantidade, preco, data_operacao FROM acoes WHERE usuario_id = ? AND ativo = ?';
$params = [$usuario_id, $ativo];
if ($ignorar_id !== null) {
$sql .= ' AND id != ?';
$params[] = $ignorar_id;
}
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$linhas = $stmt->fetchAll();
foreach ($linhas as &$l) {
$l['_ordem'] = (int) $l['id'];
}
return $linhas;
}
/**
* Recalcula e salva a posição consolidada (quantidade total + PM) de um
* ativo para um usuário, simulando todo o histórico de compras/vendas dele.
* Se não sobrar quantidade nenhuma, a posição é removida.
*/
function ao_recalcular_posicao_acao(PDO $pdo, $usuario_id, $ativo) {
$linhas = ao_buscar_transacoes_ativo($pdo, $usuario_id, $ativo);
$sim = ao_simular_transacoes($linhas);
if ($sim['qtd'] <= 0) {
$pdo->prepare('DELETE FROM acoes_posicoes WHERE usuario_id = ? AND ativo = ?')->execute([$usuario_id, $ativo]);
return;
}
// A categoria do ativo é a mesma em todas as operações dele (garantido na
// validação do save) — pega de qualquer uma para gravar na posição.
$stmtCategoria = $pdo->prepare('SELECT categoria FROM acoes WHERE usuario_id = ? AND ativo = ? LIMIT 1');
$stmtCategoria->execute([$usuario_id, $ativo]);
$categoria = $stmtCategoria->fetchColumn() ?: 'acao';
$stmt = $pdo->prepare(
'INSERT INTO acoes_posicoes (usuario_id, ativo, categoria, quantidade, preco_medio) VALUES (?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE categoria = VALUES(categoria), quantidade = VALUES(quantidade), preco_medio = VALUES(preco_medio)'
);
$stmt->execute([$usuario_id, $ativo, $categoria, $sim['qtd'], $sim['pm']]);
}
/** Converte "38,42" ou "38.42" em float. Retorna null se não for um número válido. */
function ao_parse_decimal_br($valor) {
$valor = str_replace(',', '.', trim((string) $valor));
return is_numeric($valor) ? (float) $valor : null;
}
/** Badge colorido com o rótulo (Ação/ETF/BDR/FII) de uma categoria de ativo. */
function ao_badge_categoria($categoria) {
$classes = ['acao' => 'ao-badge-neutro', 'etf' => 'ao-badge-info', 'bdr' => 'ao-badge-alerta', 'fii' => 'ao-badge-fii'];
$classe = $classes[$categoria] ?? 'ao-badge-neutro';
$rotulo = AO_CATEGORIAS_ATIVO[$categoria] ?? $categoria;
return '<span class="ao-badge ' . $classe . '">' . htmlspecialchars($rotulo) . '</span>';
}
$erro = null;
$sucesso = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['acao'] ?? '') === 'excluir') {
$id = (int) ($_POST['id'] ?? 0);
$stmt = $pdo->prepare('SELECT ativo FROM acoes WHERE id = ? AND usuario_id = ?');
$stmt->execute([$id, $ao_usuario_id]);
$linha = $stmt->fetch();
if ($linha) {
// Antes de excluir, confere se remover esta operação deixaria alguma
// venda posterior sem ações suficientes (ex: excluir uma compra cujas
// ações já foram vendidas depois).
$sim = ao_simular_transacoes(ao_buscar_transacoes_ativo($pdo, $ao_usuario_id, $linha['ativo'], $id));
if (!$sim['ok']) {
$erro = 'Não é possível excluir: isso deixaria uma venda sem ações suficientes na carteira.';
} else {
$pdo->prepare('DELETE FROM acoes WHERE id = ? AND usuario_id = ?')->execute([$id, $ao_usuario_id]);
ao_recalcular_posicao_acao($pdo, $ao_usuario_id, $linha['ativo']);
header('Location: acoes.php?excluido=1');
exit;
}
} else {
header('Location: acoes.php?excluido=1');
exit;
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['acao'] ?? '') === 'salvar') {
$id = (int) ($_POST['id'] ?? 0);
$conta_id = (int) ($_POST['conta_id'] ?? 0);
$ativo = strtoupper(trim($_POST['ativo'] ?? ''));
$categoria = (string) ($_POST['categoria'] ?? '');
$tipo = ($_POST['tipo'] ?? '') === 'venda' ? 'venda' : 'compra';
$quantidade = (int) ($_POST['quantidade'] ?? 0);
$data_operacao = trim($_POST['data_operacao'] ?? '');
$preco_bruto = trim($_POST['preco'] ?? '');
$preco = ao_parse_decimal_br($preco_bruto);
$ativo_antigo = null;
if (!array_key_exists($categoria, AO_CATEGORIAS_ATIVO)) {
$categoria = 'acao';
}
// A conta precisa existir e pertencer ao usuário logado — impede que
// alguém forje o id de uma conta de outra pessoa no formulário.
$stmtConta = $pdo->prepare('SELECT id FROM contas WHERE id = ? AND usuario_id = ?');
$stmtConta->execute([$conta_id, $ao_usuario_id]);
$contaValida = (bool) $stmtConta->fetch();
if (!$contaValida) {
$erro = 'Selecione uma conta válida.';
} elseif ($ativo === '' || $quantidade <= 0 || $preco === null || $preco <= 0) {
$erro = 'Preencha código do ativo, quantidade (maior que zero) e preço (maior que zero) corretamente.';
} elseif ($data_operacao !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $data_operacao)) {
$erro = 'Data da operação inválida.';
} else {
$ignorar_id = $id > 0 ? $id : null;
if ($id > 0) {
$stmtAntigo = $pdo->prepare('SELECT ativo FROM acoes WHERE id = ? AND usuario_id = ?');
$stmtAntigo->execute([$id, $ao_usuario_id]);
$antigo = $stmtAntigo->fetch();
if (!$antigo) {
$erro = 'Operação não encontrada.';
} else {
$ativo_antigo = $antigo['ativo'];
}
}
if (!$erro) {
// A categoria é do ativo, não da operação — todas as operações de
// um mesmo código precisam concordar (evita PETR4 virar "ETF" numa
// operação e "Ação" noutra por engano).
$sqlCategoriaExistente = 'SELECT DISTINCT categoria FROM acoes WHERE usuario_id = ? AND ativo = ?';
$paramsCategoriaExistente = [$ao_usuario_id, $ativo];
if ($ignorar_id !== null) {
$sqlCategoriaExistente .= ' AND id != ?';
$paramsCategoriaExistente[] = $ignorar_id;
}
$stmtCategoriaExistente = $pdo->prepare($sqlCategoriaExistente);
$stmtCategoriaExistente->execute($paramsCategoriaExistente);
$categoriaExistente = $stmtCategoriaExistente->fetchColumn();
if ($categoriaExistente !== false && $categoriaExistente !== $categoria) {
$erro = 'Já existe(m) operação(ões) de ' . htmlspecialchars($ativo) . ' cadastrada(s) como "'
. htmlspecialchars(AO_CATEGORIAS_ATIVO[$categoriaExistente]) . '". Selecione a mesma categoria '
. 'para manter a carteira consistente (ou edite as operações antigas se o código estiver errado).';
}
}
if (!$erro) {
// Valida o ativo de destino incluindo esta operação (nova ou editada).
$linhasDestino = ao_buscar_transacoes_ativo($pdo, $ao_usuario_id, $ativo, $ignorar_id);
$linhasDestino[] = [
'tipo' => $tipo, 'quantidade' => $quantidade, 'preco' => $preco,
'data_operacao' => $data_operacao !== '' ? $data_operacao : null,
'_ordem' => $id > 0 ? $id : PHP_INT_MAX,
];
$simDestino = ao_simular_transacoes($linhasDestino);
if (!$simDestino['ok']) {
$erro = 'Essa venda deixaria a quantidade de ' . htmlspecialchars($ativo) . ' negativa em alguma data — '
. 'você não pode vender mais ações do que possuía até aquele momento.';
} elseif ($ativo_antigo !== null && $ativo_antigo !== $ativo) {
// O código do ativo mudou nesta edição: confere se o ativo antigo
// continua consistente sem esta operação (que está saindo dele).
$simAntigo = ao_simular_transacoes(ao_buscar_transacoes_ativo($pdo, $ao_usuario_id, $ativo_antigo, $id));
if (!$simAntigo['ok']) {
$erro = 'Não é possível mover esta operação para outro ativo: sobraria uma venda de '
. htmlspecialchars($ativo_antigo) . ' sem ações suficientes.';
}
}
}
if (!$erro) {
try {
if ($id > 0) {
$stmt = $pdo->prepare('UPDATE acoes SET conta_id=?, ativo=?, categoria=?, tipo=?, quantidade=?, data_operacao=?, preco=? WHERE id=? AND usuario_id=?');
$stmt->execute([$conta_id, $ativo, $categoria, $tipo, $quantidade, $data_operacao !== '' ? $data_operacao : null, $preco, $id, $ao_usuario_id]);
} else {
$stmt = $pdo->prepare('INSERT INTO acoes (usuario_id, conta_id, ativo, categoria, tipo, quantidade, data_operacao, preco) VALUES (?,?,?,?,?,?,?,?)');
$stmt->execute([$ao_usuario_id, $conta_id, $ativo, $categoria, $tipo, $quantidade, $data_operacao !== '' ? $data_operacao : null, $preco]);
}
ao_recalcular_posicao_acao($pdo, $ao_usuario_id, $ativo);
if ($ativo_antigo !== null && $ativo_antigo !== $ativo) {
ao_recalcular_posicao_acao($pdo, $ao_usuario_id, $ativo_antigo);
}
header('Location: acoes.php?salvo=1');
exit;
} catch (PDOException $e) {
$erro = 'Erro ao salvar: ' . $e->getMessage();
}
}
}
}
$editando = null;
if (isset($_GET['editar'])) {
$stmt = $pdo->prepare('SELECT * FROM acoes WHERE id = ? AND usuario_id = ?');
$stmt->execute([(int) $_GET['editar'], $ao_usuario_id]);
$editando = $stmt->fetch() ?: null;
}
// Se deu erro no envio, mantemos o que foi digitado na tela em vez de limpar tudo.
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['acao'] ?? '') === 'salvar' && $erro && $erro !== 'Operação não encontrada.') {
$editando = [
'id' => $id,
'conta_id' => $conta_id,
'ativo' => $ativo,
'categoria' => $categoria,
'tipo' => $tipo,
'quantidade' => $quantidade,
'data_operacao' => $data_operacao,
'preco' => $preco_bruto,
];
}
if (isset($_GET['salvo'])) $sucesso = 'Operação salva com sucesso.';
if (isset($_GET['excluido'])) $sucesso = 'Operação excluída.';
$stmtContas = $pdo->prepare(
'SELECT ct.id, ct.nome AS conta_nome, cr.nome AS corretora_nome
FROM contas ct
INNER JOIN corretoras cr ON cr.id = ct.corretora_id
WHERE ct.usuario_id = ?
ORDER BY cr.nome, ct.nome'
);
$stmtContas->execute([$ao_usuario_id]);
$contas = $stmtContas->fetchAll();
$stmtPosicoes = $pdo->prepare('SELECT * FROM acoes_posicoes WHERE usuario_id = ? ORDER BY ativo');
$stmtPosicoes->execute([$ao_usuario_id]);
$posicoes = $stmtPosicoes->fetchAll();
$stmtOperacoes = $pdo->prepare(
'SELECT a.*, ct.nome AS conta_nome, cr.nome AS corretora_nome
FROM acoes a
LEFT JOIN contas ct ON ct.id = a.conta_id
LEFT JOIN corretoras cr ON cr.id = ct.corretora_id
WHERE a.usuario_id = ?
ORDER BY (a.data_operacao IS NULL), a.data_operacao DESC, a.id DESC'
);
$stmtOperacoes->execute([$ao_usuario_id]);
$operacoes = $stmtOperacoes->fetchAll();
require __DIR__ . '/includes/header.php';
?>
<div class="ao-card-header" style="margin-bottom: var(--ao-space-4);">
<h1 class="ao-page-title" style="margin: 0;">Minha carteira de ações</h1>
<?php if ($contas): ?>
<button type="button" class="ao-btn ao-btn-primary" onclick="aoAbrirModalOperacao()">+ Nova operação</button>
<?php endif; ?>
</div>
<?php if ($erro && !$editando): ?>
<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; ?>
<?php if (!$contas): ?>
<div class="ao-card" style="max-width: 560px; margin-bottom: var(--ao-space-6);">
<p style="margin: 0;">Antes de cadastrar uma operação, você precisa ter uma corretora e uma conta cadastradas.
<a href="corretoras.php">Cadastre uma corretora</a> e depois <a href="contas.php">cadastre uma conta</a> nela.</p>
</div>
<?php endif; ?>
<div class="ao-modal-backdrop<?= $editando ? ' is-open' : '' ?>" id="ao-modal-operacao">
<div class="ao-modal" role="dialog" aria-modal="true" aria-labelledby="ao-modal-operacao-titulo">
<div class="ao-modal-header">
<strong id="ao-modal-operacao-titulo"><?= ($editando && !empty($editando['id'])) ? 'Editar operação' : 'Nova operação' ?></strong>
<button type="button" class="ao-modal-close" onclick="aoFecharModalOperacao()" aria-label="Fechar">&times;</button>
</div>
<p style="color: var(--ao-text-muted); font-size: var(--ao-fs-sm); margin-top: 0;">
Cadastre cada compra ou venda separadamente, mesmo que seja do mesmo ativo em datas diferentes —
o preço médio (PM) é recalculado automaticamente: compras atualizam o PM, vendas só reduzem a
quantidade. Esta carteira é só sua.
</p>
<?php if ($erro && $editando): ?>
<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; ?>
<form method="post" action="acoes.php">
<input type="hidden" name="acao" value="salvar">
<input type="hidden" name="id" value="<?= (int) ($editando['id'] ?? 0) ?>">
<div class="ao-field" style="margin-bottom: var(--ao-space-4);">
<label for="conta_id">Conta</label>
<select id="conta_id" name="conta_id" required>
<option value="">Selecione...</option>
<?php foreach ($contas as $ct): ?>
<option value="<?= $ct['id'] ?>" <?= (isset($editando) && (int) ($editando['conta_id'] ?? 0) === (int) $ct['id']) ? 'selected' : '' ?>>
<?= htmlspecialchars($ct['corretora_nome']) ?> — <?= htmlspecialchars($ct['conta_nome']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="ao-field" style="margin-bottom: var(--ao-space-4);">
<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($editando['ativo'] ?? '') ?>">
</div>
<div class="ao-row" style="gap: var(--ao-space-4); margin-bottom: var(--ao-space-4);">
<div class="ao-field" style="flex: 1;">
<label for="categoria">Tipo de ativo</label>
<select id="categoria" name="categoria">
<?php foreach (AO_CATEGORIAS_ATIVO as $ao_cat_chave => $ao_cat_rotulo): ?>
<option value="<?= $ao_cat_chave ?>" <?= (($editando['categoria'] ?? 'acao') === $ao_cat_chave) ? 'selected' : '' ?>><?= htmlspecialchars($ao_cat_rotulo) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="ao-field" style="width: 150px;">
<label for="tipo">Tipo</label>
<select id="tipo" name="tipo">
<option value="compra" <?= (($editando['tipo'] ?? 'compra') === 'compra') ? 'selected' : '' ?>>Compra</option>
<option value="venda" <?= (($editando['tipo'] ?? '') === 'venda') ? 'selected' : '' ?>>Venda</option>
</select>
</div>
</div>
<div class="ao-row" style="gap: var(--ao-space-4); margin-bottom: var(--ao-space-4);">
<div class="ao-field" style="flex: 1;">
<label for="quantidade">Quantidade</label>
<input type="number" id="quantidade" name="quantidade" min="1" step="1" placeholder="100" required
value="<?= htmlspecialchars((string) ($editando['quantidade'] ?? '')) ?>">
</div>
<div class="ao-field" style="flex: 1;">
<label for="data_operacao">Data da operação (se tiver)</label>
<input type="date" id="data_operacao" name="data_operacao" value="<?= htmlspecialchars($editando['data_operacao'] ?? '') ?>">
</div>
</div>
<div class="ao-field" style="margin-bottom: var(--ao-space-5);">
<label for="preco">Preço</label>
<input type="text" id="preco" name="preco" placeholder="Ex: 38,42" required
value="<?= htmlspecialchars((string) ($editando['preco'] ?? '')) ?>">
</div>
<button type="submit" class="ao-btn ao-btn-primary"><?= ($editando && !empty($editando['id'])) ? 'Salvar alterações' : 'Cadastrar operação' ?></button>
<button type="button" class="ao-btn ao-btn-secondary" onclick="aoFecharModalOperacao()">Cancelar</button>
</form>
</div>
</div>
<div class="ao-card" style="margin-bottom: var(--ao-space-6);">
<div class="ao-card-header"><strong>Carteira consolidada</strong></div>
<table class="ao-table">
<thead>
<tr><th>Ativo</th><th>Categoria</th><th>Quantidade</th><th>Preço médio (PM)</th><th>Valor investido</th></tr>
</thead>
<tbody>
<?php if (!$posicoes): ?>
<tr><td colspan="5" style="color: var(--ao-text-muted);">Nenhuma ação em carteira no momento.</td></tr>
<?php endif; ?>
<?php foreach ($posicoes as $p): ?>
<tr>
<td class="ao-numeric"><?= htmlspecialchars($p['ativo']) ?></td>
<td><?= ao_badge_categoria($p['categoria']) ?></td>
<td class="ao-numeric"><?= (int) $p['quantidade'] ?></td>
<td class="ao-numeric">R$ <?= number_format((float) $p['preco_medio'], 2, ',', '.') ?></td>
<td class="ao-numeric">R$ <?= number_format((float) $p['preco_medio'] * (int) $p['quantidade'], 2, ',', '.') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="ao-card">
<div class="ao-card-header"><strong>Histórico de operações</strong></div>
<table class="ao-table">
<thead>
<tr><th>Data</th><th>Ativo</th><th>Categoria</th><th>Conta</th><th>Tipo</th><th>Quantidade</th><th>Preço</th><th></th></tr>
</thead>
<tbody>
<?php if (!$operacoes): ?>
<tr><td colspan="8" style="color: var(--ao-text-muted);">Nenhuma operação cadastrada ainda.</td></tr>
<?php endif; ?>
<?php foreach ($operacoes as $o): ?>
<tr>
<td class="ao-numeric"><?= $o['data_operacao'] ? htmlspecialchars(date('d/m/Y', strtotime($o['data_operacao']))) : '—' ?></td>
<td class="ao-numeric"><?= htmlspecialchars($o['ativo']) ?></td>
<td><?= ao_badge_categoria($o['categoria']) ?></td>
<td><?= $o['conta_nome'] ? htmlspecialchars($o['corretora_nome'] . ' — ' . $o['conta_nome']) : '—' ?></td>
<td><span class="ao-badge <?= $o['tipo'] === 'compra' ? 'ao-badge-alta' : 'ao-badge-baixa' ?>"><?= $o['tipo'] === 'compra' ? 'Compra' : 'Venda' ?></span></td>
<td class="ao-numeric"><?= (int) $o['quantidade'] ?></td>
<td class="ao-numeric">R$ <?= number_format((float) $o['preco'], 2, ',', '.') ?></td>
<td style="white-space: nowrap; text-align: right;">
<a href="acoes.php?editar=<?= $o['id'] ?>" class="ao-btn ao-btn-secondary" style="padding: 4px 10px; font-size: var(--ao-fs-xs);">Editar</a>
<form method="post" action="acoes.php" style="display:inline;" onsubmit="return confirm('Excluir esta operação? O preço médio do ativo será recalculado.');">
<input type="hidden" name="acao" value="excluir">
<input type="hidden" name="id" value="<?= $o['id'] ?>">
<button type="submit" class="ao-btn ao-btn-secondary" style="padding: 4px 10px; font-size: var(--ao-fs-xs);">Excluir</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<style>
/* FII não tem uma cor "pronta" nos badges do sistema (alta/baixa/alerta/info
ficam reservadas para ganho/perda e para os estados de conta/permissão) —
usa a cor de marca (--ao-primary) num tom suave, só para diferenciar. */
.ao-badge-fii { background: var(--ao-primary-soft); color: var(--ao-primary); }
</style>
<script>
function aoAbrirModalOperacao() {
document.getElementById('ao-modal-operacao').classList.add('is-open');
}
function aoFecharModalOperacao() {
document.getElementById('ao-modal-operacao').classList.remove('is-open');
// Se a modal foi aberta por um link de edição (?editar=...), limpa a URL ao fechar.
if (window.location.search) {
window.history.replaceState(null, '', 'acoes.php');
}
}
document.getElementById('ao-modal-operacao').addEventListener('click', function (evento) {
if (evento.target === this) aoFecharModalOperacao();
});
document.addEventListener('keydown', function (evento) {
if (evento.key === 'Escape') aoFecharModalOperacao();
});
</script>
<?php require __DIR__ . '/includes/footer.php'; ?>