Files
rm-PAGOs/pagos/api/movimientos.php
T
Ricardo MonlaandClaude Sonnet 4.6 1db200343d Feature: Historial, DELETE de movimientos y sistema de modales
- Nueva pantalla historial.html con movimientos agrupados por mes
- Botón eliminar en cada movimiento con modal de confirmación
- js/ui.js: sistema de modales y toasts reutilizables (reemplaza alert/confirm nativos)
- API: endpoint GET ?view=historial y método DELETE en movimientos.php
- add.html: categorías cargadas dinámicamente desde la API
- index.html: "Ver todo" e ícono de nav apuntan a historial.html
- Docker: eliminado volumen nombrado db_data para evitar problemas de permisos

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 16:06:28 -03:00

110 lines
3.9 KiB
PHP

<?php
// pagos/api/movimientos.php
require_once 'auth.php'; // Protección de acceso
header('Content-Type: application/json');
require_once 'db.php';
$pdo = getDB();
$method = $_SERVER['REQUEST_METHOD'];
if ($method === 'GET') {
$view = $_GET['view'] ?? 'dashboard';
try {
if ($view === 'historial') {
$stmt = $pdo->query("SELECT m.*, c.nombre as categoria_nombre, c.icono as categoria_icono
FROM movimientos m
LEFT JOIN categorias c ON m.id_categoria = c.id
ORDER BY m.fecha DESC, m.id DESC");
echo json_encode([
'status' => 'success',
'movimientos' => $stmt->fetchAll(PDO::FETCH_ASSOC)
]);
exit;
}
// Dashboard view
$stmtBalance = $pdo->query("SELECT
SUM(CASE WHEN tipo = 'ingreso' THEN monto ELSE -monto END) as balance
FROM movimientos");
$balance = $stmtBalance->fetch(PDO::FETCH_ASSOC)['balance'] ?? 0;
$stmtMovi = $pdo->query("SELECT m.*, c.nombre as categoria_nombre, c.icono as categoria_icono
FROM movimientos m
LEFT JOIN categorias c ON m.id_categoria = c.id
ORDER BY m.fecha DESC, m.id DESC LIMIT 5");
$movimientos = $stmtMovi->fetchAll(PDO::FETCH_ASSOC);
$stmtGraph = $pdo->query("SELECT c.nombre, c.color_hex, SUM(m.monto) as total
FROM movimientos m
JOIN categorias c ON m.id_categoria = c.id
WHERE m.tipo = 'gasto'
GROUP BY c.id
ORDER BY total DESC");
$grafico = $stmtGraph->fetchAll(PDO::FETCH_ASSOC);
$stmtCat = $pdo->query("SELECT * FROM categorias");
$categorias = $stmtCat->fetchAll(PDO::FETCH_ASSOC);
echo json_encode([
'status' => 'success',
'balance' => (float)$balance,
'movimientos' => $movimientos,
'grafico' => $grafico,
'categorias' => $categorias
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
} elseif ($method === 'DELETE') {
$data = json_decode(file_get_contents('php://input'), true);
if (!isset($data['id'])) {
http_response_code(400);
die(json_encode(['status' => 'error', 'message' => 'Falta el ID']));
}
try {
$stmt = $pdo->prepare("DELETE FROM movimientos WHERE id = ?");
$stmt->execute([(int) $data['id']]);
if ($stmt->rowCount() === 0) {
http_response_code(404);
echo json_encode(['status' => 'error', 'message' => 'Movimiento no encontrado']);
} else {
echo json_encode(['status' => 'success']);
}
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
} elseif ($method === 'POST') {
// Insertar nuevo movimiento
$data = json_decode(file_get_contents('php://input'), true);
// Validación mínima
if (!isset($data['tipo'], $data['monto'], $data['id_categoria'], $data['fecha'])) {
http_response_code(400);
die(json_encode(['status' => 'error', 'message' => 'Faltan campos obligatorios']));
}
try {
$stmt = $pdo->prepare("INSERT INTO movimientos (tipo, monto, fecha, notas, id_categoria) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([
$data['tipo'],
(float) $data['monto'],
$data['fecha'],
$data['notas'] ?? '',
(int) $data['id_categoria']
]);
echo json_encode(['status' => 'success', 'id' => $pdo->lastInsertId()]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
} else {
http_response_code(405);
echo json_encode(['status' => 'error', 'message' => 'Método no permitido']);
}
?>