diff --git a/accounts.php b/accounts.php new file mode 100644 index 0000000..1c2b153 --- /dev/null +++ b/accounts.php @@ -0,0 +1,181 @@ +query('SELECT id, name FROM users ORDER BY name'); +$users = $user_stmt->fetchAll(); + +// Handle form submissions for adding/editing an account +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $name = trim($_POST['name']); + $user_id = $_POST['user_id'] ?? null; + $initial_balance = $_POST['initial_balance'] ?? 0; + $currency = $_POST['currency'] ?? 'USD'; + $id = $_POST['id'] ?? null; + + if (empty($name)) { + $error = 'Account name is required.'; + } else { + try { + if ($id) { + // Update existing account + $stmt = $pdo->prepare('UPDATE accounts SET name = ?, user_id = ?, initial_balance = ?, currency = ? WHERE id = ?'); + $stmt->execute([$name, $user_id, $initial_balance, $currency, $id]); + $message = 'Account updated successfully!'; + } else { + // Insert new account + $stmt = $pdo->prepare('INSERT INTO accounts (name, user_id, initial_balance, currency) VALUES (?, ?, ?, ?)'); + $stmt->execute([$name, $user_id, $initial_balance, $currency]); + $message = 'Account added successfully!'; + } + } catch (PDOException $e) { + $error = 'Database error: ' . $e->getMessage(); + } + } +} + +// Handle deleting an account +if (isset($_GET['delete'])) { + $id = $_GET['delete']; + try { + $stmt = $pdo->prepare('DELETE FROM accounts WHERE id = ?'); + $stmt->execute([$id]); + $message = 'Account deleted successfully!'; + } catch (PDOException $e) { + $error = 'Error deleting account. It might be associated with expenses.'; + } +} + +// Handle fetching an account for editing +if (isset($_GET['edit'])) { + $id = $_GET['edit']; + $stmt = $pdo->prepare('SELECT * FROM accounts WHERE id = ?'); + $stmt->execute([$id]); + $edit_account = $stmt->fetch(); +} + +// Fetch all accounts to display +$stmt = $pdo->query('SELECT a.*, u.name as user_name FROM accounts a LEFT JOIN users u ON a.user_id = u.id ORDER BY a.name'); +$accounts = $stmt->fetchAll(); + +$currencies = ['USD', 'EUR', 'COP']; + +?> + + + + + + Manage Accounts + + + + + +
+
+

Manage Accounts

+ Back to Dashboard +
+ + +
+ + +
+ + + +
+
+
+
+
+
+ +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + + Cancel Edit + +
+
+
+ + +
+
+
Existing Accounts
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOwnerBalanceCurrencyActions
No accounts found.
+ Edit + Delete +
+
+
+
+ + + + diff --git a/add_expense.php b/add_expense.php index 8424cf2..18540c2 100644 --- a/add_expense.php +++ b/add_expense.php @@ -1,3 +1,63 @@ +query('SELECT * FROM categories ORDER BY name')->fetchAll(); +$users = $pdo->query('SELECT * FROM users ORDER BY name')->fetchAll(); +$accounts = $pdo->query('SELECT * FROM accounts ORDER BY name')->fetchAll(); + +$expense_types = ['expense', 'income', 'transfer']; +$split_types = ['none', 'equally', 'parts', 'amounts']; +$currencies = ['USD', 'EUR', 'COP']; + +if ($_SERVER["REQUEST_METHOD"] == "POST") { + try { + // Sanitize and validate input + $expense_date = filter_input(INPUT_POST, 'expense_date', FILTER_SANITIZE_STRING); + $title = filter_input(INPUT_POST, 'title', FILTER_SANITIZE_STRING); + $amount = filter_input(INPUT_POST, 'amount', FILTER_VALIDATE_FLOAT); + $currency = filter_input(INPUT_POST, 'currency', FILTER_SANITIZE_STRING); + + $category_id = filter_input(INPUT_POST, 'category_id', FILTER_VALIDATE_INT); + $user_id = filter_input(INPUT_POST, 'user_id', FILTER_VALIDATE_INT); + $account_id = filter_input(INPUT_POST, 'account_id', FILTER_VALIDATE_INT); + $expense_type = filter_input(INPUT_POST, 'expense_type', FILTER_SANITIZE_STRING); + $split_type = filter_input(INPUT_POST, 'split_type', FILTER_SANITIZE_STRING); + + // Handle file upload + $receipt_path = null; + if (isset($_FILES['receipt']) && $_FILES['receipt']['error'] == UPLOAD_ERR_OK) { + $upload_dir = __DIR__ . '/assets/uploads/'; + if (!is_dir($upload_dir)) { + mkdir($upload_dir, 0775, true); + } + $filename = uniqid() . '-' . basename($_FILES['receipt']['name']); + $receipt_path = '/assets/uploads/' . $filename; + move_uploaded_file($_FILES['receipt']['tmp_name'], $upload_dir . $filename); + } + + $sql = "INSERT INTO expenses (expense_date, title, amount, currency, expense_type, split_type, category_id, user_id, account_id, receipt_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + $stmt = $pdo->prepare($sql); + + if ($stmt->execute([$expense_date, $title, $amount, $currency, $expense_type, $split_type, $category_id, $user_id, $account_id, $receipt_path])) { + header("Location: index.php?message=success"); + exit; + } else { + $error = "Error saving the expense."; + } + } catch (PDOException $e) { + error_log("DB Error: " . $e->getMessage()); + $error = "Database error. Please try again later."; + } catch (Exception $e) { + error_log("File Upload Error: " . $e->getMessage()); + $error = "Error uploading the file."; + } +} +?> @@ -14,72 +74,90 @@
+ +
+

Añadir Nuevo Gasto

-
+
- +
- - + +
+
- - -
-
- - -
-
- +
$ - +
- - + + +
-
- -
-
- - -
-
- - -
-
+ +
+ +
+
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+
- - + +
@@ -99,4 +177,4 @@ - \ No newline at end of file + diff --git a/categories.php b/categories.php new file mode 100644 index 0000000..4c41287 --- /dev/null +++ b/categories.php @@ -0,0 +1,145 @@ +prepare('UPDATE categories SET name = ? WHERE id = ?'); + $stmt->execute([$name, $id]); + $message = 'Category updated successfully!'; + } else { + // Insert new category + $stmt = $pdo->prepare('INSERT INTO categories (name) VALUES (?)'); + $stmt->execute([$name]); + $message = 'Category added successfully!'; + } + } catch (PDOException $e) { + if ($e->errorInfo[1] == 1062) { // Duplicate entry + $error = 'Category name already exists.'; + } else { + $error = 'Database error: ' . $e->getMessage(); + } + } + } +} + +// Handle deleting a category +if (isset($_GET['delete'])) { + $id = $_GET['delete']; + try { + $stmt = $pdo->prepare('DELETE FROM categories WHERE id = ?'); + $stmt->execute([$id]); + $message = 'Category deleted successfully!'; + } catch (PDOException $e) { + $error = 'Error deleting category. It might be in use.'; + } +} + +// Handle fetching a category for editing +if (isset($_GET['edit'])) { + $id = $_GET['edit']; + $stmt = $pdo->prepare('SELECT * FROM categories WHERE id = ?'); + $stmt->execute([$id]); + $edit_category = $stmt->fetch(); +} + +// Fetch all categories to display +$stmt = $pdo->query('SELECT * FROM categories ORDER BY name'); +$categories = $stmt->fetchAll(); + +?> + + + + + + Manage Categories + + + + + +
+
+

Manage Categories

+ Back to Dashboard +
+ + +
+ + +
+ + + +
+
+
+
+
+ + +
+ + +
+ + + Cancel Edit + + +
+
+ + +
+
+
Existing Categories
+
+
+ + + + + + + + + + + + + + + + + + + + + +
NameActions
No categories found.
+ Edit + Delete +
+
+
+
+ + + + diff --git a/db/migrations/002_add_entities.sql b/db/migrations/002_add_entities.sql new file mode 100644 index 0000000..d623428 --- /dev/null +++ b/db/migrations/002_add_entities.sql @@ -0,0 +1,41 @@ +-- Create categories table +CREATE TABLE IF NOT EXISTS `categories` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(255) NOT NULL UNIQUE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- Create users table +CREATE TABLE IF NOT EXISTS `users` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(255) NOT NULL, + `email` VARCHAR(255) NOT NULL UNIQUE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- Create accounts table +CREATE TABLE IF NOT EXISTS `accounts` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(255) NOT NULL, + `user_id` INT, + `initial_balance` DECIMAL(15, 2) NOT NULL DEFAULT 0.00, + `currency` ENUM('USD', 'EUR', 'COP') NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- Step 1: Add new columns to 'expenses' table without dropping old ones yet. +ALTER TABLE `expenses` + ADD COLUMN `category_id` INT NULL AFTER `receipt_path`, + ADD COLUMN `user_id` INT NULL AFTER `category_id`, + ADD COLUMN `account_id` INT NULL AFTER `user_id`, + ADD COLUMN `expense_type` ENUM('expense', 'income', 'transfer') NOT NULL DEFAULT 'expense' AFTER `amount`, + ADD COLUMN `split_type` ENUM('equally', 'parts', 'amounts', 'none') NOT NULL DEFAULT 'none' AFTER `expense_type`; + +-- Step 2: Add foreign key constraints +-- Note: We'll add constraints in a separate step after data migration to avoid issues with existing data. +-- ALTER TABLE `expenses` +-- ADD CONSTRAINT `fk_category` FOREIGN KEY (`category_id`) REFERENCES `categories`(`id`) ON DELETE SET NULL, +-- ADD CONSTRAINT `fk_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL, +-- ADD CONSTRAINT `fk_account` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`) ON DELETE SET NULL; + +-- Step 3: Modify the currency column +ALTER TABLE `expenses` + MODIFY COLUMN `currency` ENUM('USD', 'EUR', 'COP') NOT NULL; diff --git a/export.php b/export.php new file mode 100644 index 0000000..bf3497a --- /dev/null +++ b/export.php @@ -0,0 +1,52 @@ +query(" + SELECT + e.expense_date, + e.title, + c.name as category_name, + a.name as account_name, + u.name as user_name, + e.expense_type, + e.amount, + e.currency + FROM + expenses e + LEFT JOIN + categories c ON e.category_id = c.id + LEFT JOIN + users u ON e.user_id = u.id + LEFT JOIN + accounts a ON e.account_id = a.id + ORDER BY + e.expense_date DESC + "); + + $filename = "expenses_" . date('Y-m-d') . ".csv"; + + header('Content-Type: text/csv; charset=utf-8'); + header('Content-Disposition: attachment; filename=' . $filename); + + $output = fopen('php://output', 'w'); + + // Add UTF-8 BOM to prevent issues with special characters in Excel + fputs($output, "\xEF\xBB\xBF"); + + // Header row + fputcsv($output, ['Date', 'Title', 'Category', 'Account', 'User', 'Type', 'Amount', 'Currency']); + + // Data rows + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + fputcsv($output, $row); + } + + fclose($output); + exit; + +} catch (PDOException $e) { + error_log("Export Error: " . $e->getMessage()); + die("An error occurred during export. Please check the logs."); +} diff --git a/import.php b/import.php new file mode 100644 index 0000000..deb0c26 --- /dev/null +++ b/import.php @@ -0,0 +1,123 @@ +prepare("SELECT id FROM `$table` WHERE `$name_column` = ?"); + $stmt->execute([$name_value]); + $id = $stmt->fetchColumn(); + + if (!$id) { + $cols = '`' . $name_column . '`'; + $vals = '?'; + $exec_vals = [$name_value]; + + foreach ($extra_cols as $col => $val) { + $cols .= ', '`' . $col . '`'; + $vals .= ', ?'; + $exec_vals[] = $val; + } + + $stmt = $pdo->prepare("INSERT INTO `$table` ($cols) VALUES ($vals)"); + $stmt->execute($exec_vals); + $id = $pdo->lastInsertId(); + } + return $id; +} + +if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_FILES['csv_file'])) { + $file = $_FILES['csv_file']['tmp_name']; + + if (($handle = fopen($file, "r")) !== FALSE) { + $pdo = db(); + $pdo->beginTransaction(); + try { + // Skip header row + fgetcsv($handle, 1000, ","); + + $imported_count = 0; + $sql = "INSERT INTO expenses (expense_date, title, amount, currency, expense_type, category_id, user_id, account_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + $stmt = $pdo->prepare($sql); + + while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) { + // CSV format: Date,Title,Category,Account,User,Type,Amount,Currency + $date = $data[0]; + $title = $data[1]; + $category_name = $data[2]; + $account_name = $data[3]; + $user_name = $data[4]; + $expense_type = $data[5]; + $amount = $data[6]; + $currency = $data[7]; + + $category_id = find_or_create($pdo, 'categories', 'name', $category_name); + $user_id = find_or_create($pdo, 'users', 'name', $user_name, ['email' => strtolower(str_replace(' ', '.', $user_name)).'@example.com']); + $account_id = find_or_create($pdo, 'accounts', 'name', $account_name, ['user_id' => $user_id, 'currency' => $currency]); + + $stmt->execute([$date, $title, $amount, $currency, $expense_type, $category_id, $user_id, $account_id]); + $imported_count++; + } + + $pdo->commit(); + $message = "Successfully imported $imported_count expenses!"; + + } catch (Exception $e) { + $pdo->rollBack(); + $error = "Import failed: " . $e->getMessage(); + } + fclose($handle); + } else { + $error = "Could not open the uploaded file."; + } +} +?> + + + + + + Import Expenses + + + + + +
+
+

Import Expenses from CSV

+ Back to Dashboard +
+ + +
+ + +
+ + +
+
+
Upload CSV File
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + diff --git a/index.php b/index.php index 1d55366..ed9b9c1 100644 --- a/index.php +++ b/index.php @@ -4,15 +4,45 @@ declare(strict_types=1); @error_reporting(E_ALL); @date_default_timezone_set('UTC'); +require_once __DIR__ . '/db/config.php'; + $phpVersion = PHP_VERSION; $now = date('Y-m-d H:i:s'); +$message = $_GET['message'] ?? ''; + +$expenses = []; +try { + $pdo = db(); + $stmt = $pdo->query(" + SELECT + e.*, + c.name as category_name, + u.name as user_name, + a.name as account_name + FROM + expenses e + LEFT JOIN + categories c ON e.category_id = c.id + LEFT JOIN + users u ON e.user_id = u.id + LEFT JOIN + accounts a ON e.account_id = a.id + ORDER BY + e.expense_date DESC + "); + $expenses = $stmt->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + error_log("DB Error: " . $e->getMessage()); + // You might want to display a user-friendly error message +} + ?> - + - New Style + Gestor de Gastos - - -
-
-
-

Gestor de Gastos Familiar

-
-

Bienvenido a tu espacio financiero compartido. Registra gastos, crea presupuestos y mantén el control de tus finanzas en pareja.

-
- Añadir Gasto - -
+ + +
+
+

Gestor de Gastos Familiar

+

Bienvenido a tu espacio financiero. Registra y controla tus gastos.

+
+ + + + + +
+
+

Resumen de Gastos

+
+
+ +
+

No hay gastos registrados. ¡Empieza añadiendo uno!

+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FechaTítuloCategoríaCuentaUsuarioTipoMonto
+ + +
+
+ +
-
-
- Page updated: (UTC) -
+
+ +
+ Page updated: (UTC) +
+ + - + \ No newline at end of file diff --git a/users.php b/users.php new file mode 100644 index 0000000..82a668b --- /dev/null +++ b/users.php @@ -0,0 +1,156 @@ +prepare('UPDATE users SET name = ?, email = ? WHERE id = ?'); + $stmt->execute([$name, $email, $id]); + $message = 'User updated successfully!'; + } else { + // Insert new user + $stmt = $pdo->prepare('INSERT INTO users (name, email) VALUES (?, ?)'); + $stmt->execute([$name, $email]); + $message = 'User added successfully!'; + } + } catch (PDOException $e) { + if ($e->errorInfo[1] == 1062) { // Duplicate entry for email + $error = 'Email address already exists.'; + } else { + $error = 'Database error: ' . $e->getMessage(); + } + } + } +} + +// Handle deleting a user +if (isset($_GET['delete'])) { + $id = $_GET['delete']; + try { + $stmt = $pdo->prepare('DELETE FROM users WHERE id = ?'); + $stmt->execute([$id]); + $message = 'User deleted successfully!'; + } catch (PDOException $e) { + $error = 'Error deleting user. They might be associated with expenses.'; + } +} + +// Handle fetching a user for editing +if (isset($_GET['edit'])) { + $id = $_GET['edit']; + $stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?'); + $stmt->execute([$id]); + $edit_user = $stmt->fetch(); +} + +// Fetch all users to display +$stmt = $pdo->query('SELECT * FROM users ORDER BY name'); +$users = $stmt->fetchAll(); + +?> + + + + + + Manage Users + + + + + +
+
+

Manage Users

+ Back to Dashboard +
+ + +
+ + +
+ + + +
+
+
+
+
+
+ +
+
+ + +
+
+ + +
+
+ + + Cancel Edit + +
+
+
+ + +
+
+
Existing Users
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
NameEmailActions
No users found.
+ Edit + Delete +
+
+
+
+ + + +