A comprehensive PHP-based financial management system with expense tracking, accounts receivables, and detailed financial reporting for Philippine businesses.
FinanceTrack is a comprehensive financial management system designed specifically for Philippine businesses to track expenses, manage accounts receivables, and monitor tax compliance, especially with BIR Form 2307. This package contains all the source code and installation instructions needed to set up FinanceTrack on your own web server.
All PHP, JavaScript, CSS, and HTML files
SQL file with complete database structure
Apache 2.4+ or Nginx (with PHP support)
PHP 8.1 or higher (compatible with Namecheap Stellar hosting)
MySQL 5.7+ or MariaDB 10.3+
mysqli, json, fileinfo, gd, session, mbstring
Minimum 50MB for application files (more needed for attachments)
upload_max_filesize: 10M+ (for check image uploads)
post_max_size: 12M+
memory_limit: 128M+
config
folder in the uploaded filesdb.php
file<?php // Database configuration define('DB_HOST', 'localhost'); // Usually 'localhost' define('DB_USER', 'your_database_username'); // The username you created define('DB_PASS', 'your_database_password'); // The password you created define('DB_NAME', 'your_database_name'); // The database name you created // Attempt to connect to MySQL database $mysqli = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); // Check connection if($mysqli === false) { die("ERROR: Could not connect. " . $mysqli->connect_error); } ?>
uploads
folder specifically, set permissions to 777 temporarily if needed for file uploadsupload_max_filesize
: 10M or higherpost_max_size
: Larger than upload_max_filesizemax_execution_time
: 60 or highermemory_limit
: 128M or higherBelow is the complete SQL schema for the FinanceTrack database. This creates all necessary tables for expenses, receivables, users, and activity logging.
-- Create database CREATE DATABASE finance_tracker; USE finance_tracker; -- Users table CREATE TABLE users ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, email VARCHAR(100) NOT NULL UNIQUE, full_name VARCHAR(100) NOT NULL, role ENUM('superadmin', 'admin', 'manager', 'accountant', 'viewer') NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, last_login DATETIME, status ENUM('active', 'inactive', 'locked') DEFAULT 'active' ); -- User activity log CREATE TABLE user_activity_log ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, user_id INT, action VARCHAR(50) NOT NULL, details TEXT, ip_address VARCHAR(45), timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL ); -- Admin activity log CREATE TABLE admin_activity_log ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, admin_id INT, action VARCHAR(50) NOT NULL, details TEXT, ip_address VARCHAR(45), timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (admin_id) REFERENCES users(id) ON DELETE SET NULL ); -- Expense categories CREATE TABLE expense_categories ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, description TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- Expenses table CREATE TABLE expenses ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, amount_before_tax DECIMAL(15, 2) NOT NULL, tax_amount DECIMAL(15, 2), total_amount DECIMAL(15, 2) NOT NULL, currency VARCHAR(10) DEFAULT 'PHP', frequency VARCHAR(50) NOT NULL, category_id INT, start_date DATE NOT NULL, end_date DATE, status VARCHAR(20) DEFAULT 'Pending', payment_reference VARCHAR(255), bir_2307_issuer VARCHAR(100), bir_2307_quarter VARCHAR(20), notes TEXT, tax_notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_by INT, FOREIGN KEY (category_id) REFERENCES expense_categories(id) ON DELETE SET NULL, FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL ); -- Clients table CREATE TABLE clients ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, contact_person VARCHAR(100), phone VARCHAR(20), email VARCHAR(100), address TEXT, tin VARCHAR(20), notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Projects table CREATE TABLE projects ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, project_id VARCHAR(100) NOT NULL, client_id INT NOT NULL, start_date DATE, end_date DATE, status ENUM('active', 'completed', 'on-hold', 'cancelled') DEFAULT 'active', description TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE CASCADE ); -- Receivables table CREATE TABLE receivables ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, project_id INT NOT NULL, invoice_number VARCHAR(100) NOT NULL, service_or_number VARCHAR(100), amount_before_tax DECIMAL(15, 2) NOT NULL, tax_amount DECIMAL(15, 2), total_amount DECIMAL(15, 2) NOT NULL, currency VARCHAR(10) DEFAULT 'PHP', billing_sent BOOLEAN DEFAULT FALSE, billing_date DATE, payment_status VARCHAR(50) DEFAULT 'Pending', payment_date DATE, bank_deposited VARCHAR(255), payment_reference VARCHAR(255), bir_2307_received BOOLEAN DEFAULT FALSE, bir_2307_issuer VARCHAR(100), bir_2307_quarter VARCHAR(20), check_image VARCHAR(255), due_date DATE, notes TEXT, tax_notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_by INT, FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL ); -- Insert default categories INSERT INTO expense_categories (name, description) VALUES ('Marketing', 'Marketing and advertising expenses'), ('Operations', 'Day-to-day operational expenses'), ('Allowance', 'Employee allowances and benefits'), ('Salary', 'Employee salaries and compensation'), ('Government', 'Government fees, taxes, and regulatory expenses'), ('Supplier', 'Payments to suppliers and vendors'); -- Insert default superadmin user INSERT INTO users (username, password, email, full_name, role) VALUES ('superadmin', '$2y$10$y.pFaJSINwJ/DZDhDRBz2eDDn.56L0UYhS1RdZajfVjyDDT9TcRfm', 'admin@example.com', 'Administrator', 'superadmin'); -- Default password: admin123
Below is the complete file structure of the FinanceTrack application. This helps you understand how the files are organized and where to find specific functionality.
finance_tracker/ │ ├── index.php # Main entry point and dashboard ├── login.php # Login page ├── logout.php # Logout script ├── register.php # User registration (admin only) ├── reset-password.php # Password reset functionality │ ├── config/ │ ├── config.php # General configuration settings │ ├── db.php # Database connection settings │ └── auth.php # Authentication functions │ ├── includes/ │ ├── header.php # Common header with navigation │ ├── footer.php # Common footer │ ├── nav.php # Left side navigation tree │ ├── functions.php # Helper functions │ └── constants.php # Application constants │ ├── api/ │ ├── expenses/ │ │ ├── create.php # Add new expense │ │ ├── read.php # Get all expenses │ │ ├── read_one.php # Get single expense │ │ ├── update.php # Update expense │ │ ├── delete.php # Delete expense │ │ └── categories.php # Manage expense categories │ │ │ ├── receivables/ │ │ ├── create.php # Add new receivable │ │ ├── read.php # Get all receivables │ │ ├── read_one.php # Get single receivable │ │ ├── update.php # Update receivable │ │ ├── delete.php # Delete receivable │ │ └── upload.php # Handle check image uploads │ │ │ ├── clients/ │ │ ├── create.php # Add new client │ │ ├── read.php # Get all clients │ │ ├── update.php # Update client │ │ └── delete.php # Delete client │ │ │ ├── projects/ │ │ ├── create.php # Add new project │ │ ├── read.php # Get all projects │ │ ├── update.php # Update project │ │ └── delete.php # Delete project │ │ │ ├── reports/ │ │ ├── dashboard.php # Dashboard summary data │ │ ├── monthly.php # Monthly reports │ │ ├── quarterly.php # Quarterly reports │ │ ├── annual.php # Annual reports │ │ └── bir_2307.php # BIR Form 2307 reports │ │ │ └── users/ │ ├── create.php # Add new user │ ├── read.php # Get all users │ ├── update.php # Update user │ ├── delete.php # Delete user │ └── reset_password.php # Reset user password │ ├── models/ │ ├── expense.php # Expense model │ ├── receivable.php # Receivable model │ ├── client.php # Client model │ ├── project.php # Project model │ ├── category.php # Category model │ ├── user.php # User model │ └── report.php # Report model │ ├── assets/ │ ├── css/ │ │ ├── style.css # Main stylesheet │ │ ├── dashboard.css # Dashboard styles │ │ ├── forms.css # Form styles │ │ └── responsive.css # Responsive design styles │ │ │ ├── js/ │ │ ├── main.js # Main JavaScript file │ │ ├── chart.js # Chart generation scripts │ │ ├── validation.js # Form validation scripts │ │ ├── expenses.js # Expense related scripts │ │ ├── receivables.js # Receivable related scripts │ │ └── users.js # User management scripts │ │ │ └── img/ │ ├── logo.png # Application logo │ ├── icons/ # UI icons │ └── backgrounds/ # Background images │ ├── uploads/ # For check images and attachments │ ├── checks/ # Check images │ └── temp/ # Temporary upload directory │ └── database.sql # Database schema
The key configuration files that need to be modified for your installation. These files control database connections, application settings, and authentication.
<?php // Database configuration define('DB_HOST', 'localhost'); // Usually 'localhost' define('DB_USER', 'your_database_username'); // The username you created define('DB_PASS', 'your_database_password'); // The password you created define('DB_NAME', 'your_database_name'); // The database name you created // Attempt to connect to MySQL database $mysqli = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); // Check connection if($mysqli === false) { die("ERROR: Could not connect. " . $mysqli->connect_error); } ?>
<?php // Application configuration define('APP_NAME', 'FinanceTrack'); define('APP_VERSION', '1.0.0'); define('TIMEZONE', 'Asia/Manila'); // Set to your timezone define('CURRENCY', 'PHP'); // Default currency define('DATE_FORMAT', 'Y-m-d'); define('DATETIME_FORMAT', 'Y-m-d H:i:s'); // Path configurations define('BASE_URL', 'https://yourdomain.com/'); // Update with your domain define('ROOT_PATH', dirname(__DIR__) . '/'); define('UPLOADS_PATH', ROOT_PATH . 'uploads/'); define('CHECKS_PATH', UPLOADS_PATH . 'checks/'); define('TEMP_PATH', UPLOADS_PATH . 'temp/'); // Session configuration define('SESSION_NAME', 'finance_tracker_session'); define('SESSION_LIFETIME', 86400); // 24 hours in seconds // Security configuration define('HASH_COST', 10); // Password hashing cost // Email configuration define('SMTP_HOST', 'smtp.example.com'); define('SMTP_PORT', 587); define('SMTP_USERNAME', 'your-email@example.com'); define('SMTP_PASSWORD', 'your-email-password'); define('SMTP_FROM_EMAIL', 'no-reply@example.com'); define('SMTP_FROM_NAME', 'FinanceTrack System'); ?>
<?php // Start session session_name(SESSION_NAME); session_set_cookie_params(SESSION_LIFETIME); session_start(); // Authentication functions function login($username, $password) { global $mysqli; $sql = "SELECT id, username, password, email, full_name, role, status FROM users WHERE username = ?"; if($stmt = $mysqli->prepare($sql)) { $stmt->bind_param("s", $username); if($stmt->execute()) { $stmt->store_result(); if($stmt->num_rows == 1) { $stmt->bind_result($id, $db_username, $db_password, $email, $full_name, $role, $status); if($stmt->fetch()) { if($status === 'active' && password_verify($password, $db_password)) { // Password is correct, start a new session session_regenerate_id(); // Store data in session variables $_SESSION["loggedin"] = true; $_SESSION["id"] = $id; $_SESSION["username"] = $db_username; $_SESSION["email"] = $email; $_SESSION["full_name"] = $full_name; $_SESSION["role"] = $role; // Update last login timestamp $update_sql = "UPDATE users SET last_login = NOW() WHERE id = ?"; if($update_stmt = $mysqli->prepare($update_sql)) { $update_stmt->bind_param("i", $id); $update_stmt->execute(); $update_stmt->close(); } // Log successful login log_activity($id, 'login', 'User logged in successfully'); return true; } else if($status !== 'active') { return "Account is $status. Please contact administrator."; } else { return "Invalid username or password."; } } } else { return "Invalid username or password."; } } else { return "Database error. Please try again later."; } $stmt->close(); } else { return "Database error. Please try again later."; } return "Unknown error occurred."; } function is_logged_in() { return isset($_SESSION["loggedin"]) && $_SESSION["loggedin"] === true; } function require_login() { if(!is_logged_in()) { header("location: login.php"); exit; } } function require_admin() { require_login(); if($_SESSION["role"] !== 'admin' && $_SESSION["role"] !== 'superadmin') { header("location: unauthorized.php"); exit; } } function require_superadmin() { require_login(); if($_SESSION["role"] !== 'superadmin') { header("location: unauthorized.php"); exit; } } function logout() { // Log logout activity if logged in if(isset($_SESSION["id"])) { log_activity($_SESSION["id"], 'logout', 'User logged out'); } // Unset all session variables $_SESSION = array(); // Destroy the session session_destroy(); } function log_activity($user_id, $action, $details = '') { global $mysqli; $sql = "INSERT INTO user_activity_log (user_id, action, details, ip_address) VALUES (?, ?, ?, ?)"; if($stmt = $mysqli->prepare($sql)) { $ip = $_SERVER['REMOTE_ADDR']; $stmt->bind_param("isss", $user_id, $action, $details, $ip); $stmt->execute(); $stmt->close(); } } ?>
These are the main PHP files that drive the application. They handle user authentication, dashboard display, and core functionality.
<?php // Include configuration files require_once 'config/config.php'; require_once 'config/db.php'; require_once 'config/auth.php'; // Check if user is already logged in if(is_logged_in()) { header("location: index.php"); exit; } // Process login form submission $login_error = ""; if($_SERVER["REQUEST_METHOD"] == "POST") { // Get username and password from form $username = trim($_POST["username"]); $password = trim($_POST["password"]); // Attempt to log in $result = login($username, $password); if($result === true) { // Login successful, redirect to dashboard header("location: index.php"); exit; } else { // Login failed, display error $login_error = $result; } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Login - <?php echo APP_NAME; ?></title> <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.4.0/css/all.min.css" rel="stylesheet"> <link href="assets/css/style.css" rel="stylesheet"> </head> <body class="bg-gray-100"> <div class="min-h-screen flex items-center justify-center"> <div class="bg-white p-8 rounded-lg shadow-md w-full max-w-md"> <div class="text-center mb-6"> <h1 class="text-3xl font-bold text-gray-800"><?php echo APP_NAME; ?></h1> <p class="text-gray-600">Sign in to your account</p> </div> <?php if(!empty($login_error)): ?> <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4"> <?php echo $login_error; ?> </div> <?php endif; ?> <form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>"> <div class="mb-4"> <label for="username" class="block text-gray-700 text-sm font-bold mb-2">Username</label> <input type="text" id="username" name="username" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required> </div> <div class="mb-6"> <label for="password" class="block text-gray-700 text-sm font-bold mb-2">Password</label> <input type="password" id="password" name="password" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required> </div> <div class="flex items-center justify-between mb-6"> <div> <input type="checkbox" id="remember" name="remember" class="mr-2"> <label for="remember" class="text-sm text-gray-700">Remember me</label> </div> <a href="reset-password.php" class="text-sm text-blue-500 hover:text-blue-700">Forgot Password?</a> </div> <div> <button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline w-full"> Sign In </button> </div> </form> </div> </div> <script src="assets/js/main.js"></script> </body> </html>
<?php // Include configuration files require_once 'config/config.php'; require_once 'config/db.php'; require_once 'config/auth.php'; // Require login to access dashboard require_login(); // Get current year and month $current_year = date('Y'); $current_month = date('m'); // Include dashboard data functions require_once 'models/report.php'; $report = new Report($mysqli); // Get dashboard data $total_expenses = $report->getTotalExpensesForYear($current_year); $total_receivables = $report->getTotalReceivablesForYear($current_year); $receivables_collected = $report->getTotalReceivedForYear($current_year); $receivables_pending = $total_receivables - $receivables_collected; // Get quarterly data $expenses_q1 = $report->getQuarterlyExpenses($current_year, 1); $expenses_q2 = $report->getQuarterlyExpenses($current_year, 2); $expenses_q3 = $report->getQuarterlyExpenses($current_year, 3); $expenses_q4 = $report->getQuarterlyExpenses($current_year, 4); $receivables_q1 = $report->getQuarterlyReceivables($current_year, 1); $receivables_q2 = $report->getQuarterlyReceivables($current_year, 2); $receivables_q3 = $report->getQuarterlyReceivables($current_year, 3); $receivables_q4 = $report->getQuarterlyReceivables($current_year, 4); // Get monthly data for charts $monthly_expenses = $report->getMonthlyExpenses($current_year); $monthly_receivables = $report->getMonthlyReceivables($current_year); $monthly_received = $report->getMonthlyReceived($current_year); // Get BIR Form 2307 data $missing_2307 = $report->getMissing2307Count(); $bir_summary = $report->getBIR2307Summary($current_year); // Include header include 'includes/header.php'; ?> <div class="content-wrapper"> <div class="content-header"> <div class="container-fluid"> <div class="row mb-2"> <div class="col-sm-6"> <h1 class="m-0">Dashboard</h1> </div> <div class="col-sm-6"> <ol class="breadcrumb float-sm-right"> <li class="breadcrumb-item"><a href="#">Home</a></li> <li class="breadcrumb-item active">Dashboard</li> </ol> </div> </div> </div> </div> <section class="content"> <div class="container-fluid"> <!-- Financial Summary --> <div class="row"> <div class="col-lg-3 col-6"> <div class="small-box bg-info"> <div class="inner"> <h3><?php echo number_format($total_receivables, 2); ?> <?php echo CURRENCY; ?></h3> <p>Total Receivables ()</p> </div> <div class="icon"> <i class="fas fa-hand-holding-usd"></i> </div> <a href="receivables.php" class="small-box-footer">More info <i class="fas fa-arrow-circle-right"></i></a> </div> </div> <div class="col-lg-3 col-6"> <div class="small-box bg-success"> <div class="inner"> <h3><?php echo number_format($receivables_collected, 2); ?> <?php echo CURRENCY; ?></h3> <p>Received Payments ()</p> </div> <div class="icon"> <i class="fas fa-money-bill-wave"></i> </div> <a href="receivables.php?status=paid" class="small-box-footer">More info <i class="fas fa-arrow-circle-right"></i></a> </div> </div> <div class="col-lg-3 col-6"> <div class="small-box bg-warning"> <div class="inner"> <h3><?php echo number_format($total_expenses, 2); ?> <?php echo CURRENCY; ?></h3> <p>Total Expenses ()</p> </div> <div class="icon"> <i class="fas fa-file-invoice-dollar"></i> </div> <a href="expenses.php" class="small-box-footer">More info <i class="fas fa-arrow-circle-right"></i></a> </div> </div> <div class="col-lg-3 col-6"> <div class="small-box bg-danger"> <div class="inner"> <h3><?php echo $missing_2307; ?></h3> <p>Missing BIR 2307 Forms</p> </div> <div class="icon"> <i class="fas fa-exclamation-triangle"></i> </div> <a href="bir.php?missing=1" class="small-box-footer">More info <i class="fas fa-arrow-circle-right"></i></a> </div> </div> </div> <!-- Monthly Financial Graph --> <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-header"> <h3 class="card-title">Monthly Financial Overview ()</h3> </div> <div class="card-body"> <canvas id="financialChart" style="min-height: 250px; height: 250px; max-height: 250px; max-width: 100%;"></canvas> </div> </div> </div> </div> <!-- Quarterly Progress --> <div class="row"> <div class="col-md-6"> <div class="card"> <div class="card-header"> <h3 class="card-title">Quarterly Expenses ()</h3> </div> <div class="card-body"> <table class="table table-bordered"> <thead> <tr> <th>Quarter</th> <th>Amount ()</th> </tr> </thead> <tbody> <tr> <td>Q1 (Jan-Mar)</td> <td><?php echo number_format($expenses_q1, 2); ?></td> </tr> <tr> <td>Q2 (Apr-Jun)</td> <td><?php echo number_format($expenses_q2, 2); ?></td> </tr> <tr> <td>Q3 (Jul-Sep)</td> <td><?php echo number_format($expenses_q3, 2); ?></td> </tr> <tr> <td>Q4 (Oct-Dec)</td> <td><?php echo number_format($expenses_q4, 2); ?></td> </tr> <tr class="bg-light"> <td><strong>Total</strong></td> <td><strong><?php echo number_format($total_expenses, 2); ?></strong></td> </tr> </tbody> </table> </div> </div> </div> <div class="col-md-6"> <div class="card"> <div class="card-header"> <h3 class="card-title">Quarterly Receivables ()</h3> </div> <div class="card-body"> <table class="table table-bordered"> <thead> <tr> <th>Quarter</th> <th>Amount ()</th> </tr> </thead> <tbody> <tr> <td>Q1 (Jan-Mar)</td> <td><?php echo number_format($receivables_q1, 2); ?></td> </tr> <tr> <td>Q2 (Apr-Jun)</td> <td><?php echo number_format($receivables_q2, 2); ?></td> </tr> <tr> <td>Q3 (Jul-Sep)</td> <td><?php echo number_format($receivables_q3, 2); ?></td> </tr> <tr> <td>Q4 (Oct-Dec)</td> <td><?php echo number_format($receivables_q4, 2); ?></td> </tr> <tr class="bg-light"> <td><strong>Total</strong></td> <td><strong><?php echo number_format($total_receivables, 2); ?></strong></td> </tr> </tbody> </table> </div> </div> </div> </div> </div> </section> </div> <?php // Include footer include 'includes/footer.php'; ?> <script> // Load chart data const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const monthlyExpenses = <?php echo json_encode(array_values($monthly_expenses)); ?>; const monthlyReceivables = <?php echo json_encode(array_values($monthly_receivables)); ?>; const monthlyReceived = <?php echo json_encode(array_values($monthly_received)); ?>; // Create financial chart const financialCtx = document.getElementById('financialChart').getContext('2d'); const financialChart = new Chart(financialCtx, { type: 'bar', data: { labels: monthNames, datasets: [{ label: 'Expenses', data: monthlyExpenses, backgroundColor: 'rgba(255, 193, 7, 0.5)', borderColor: 'rgba(255, 193, 7, 1)', borderWidth: 1 }, { label: 'Receivables', data: monthlyReceivables, backgroundColor: 'rgba(23, 162, 184, 0.5)', borderColor: 'rgba(23, 162, 184, 1)', borderWidth: 1 }, { label: 'Received Payments', data: monthlyReceived, backgroundColor: 'rgba(40, 167, 69, 0.5)', borderColor: 'rgba(40, 167, 69, 1)', borderWidth: 1, type: 'line' }] }, options: { responsive: true, scales: { y: { beginAtZero: true, title: { display: true, text: 'Amount ()' } } } } }); </script>
The application includes a set of API endpoints for handling data operations. These are used by the frontend JavaScript to perform CRUD operations without page reloads.
<?php // Headers header("Access-Control-Allow-Origin: *"); header("Content-Type: application/json; charset=UTF-8"); header("Access-Control-Allow-Methods: POST"); header("Access-Control-Max-Age: 3600"); header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"); // Include required files include_once '../../config/db.php'; include_once '../../models/expense.php'; include_once '../../config/auth.php'; // Check if user is logged in if(!is_logged_in()) { http_response_code(401); echo json_encode(array("message" => "Unauthorized. Please log in.")); exit; } // Get database connection $database = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); // Initialize expense object $expense = new Expense($database); // Get posted data $data = json_decode(file_get_contents("php://input")); // Validate required fields if( !empty($data->name) && !empty($data->amount_before_tax) && !empty($data->total_amount) && !empty($data->frequency) && !empty($data->category_id) && !empty($data->start_date) ) { // Set expense properties $expense->name = $data->name; $expense->amount_before_tax = $data->amount_before_tax; $expense->tax_amount = $data->tax_amount; $expense->total_amount = $data->total_amount; $expense->currency = !empty($data->currency) ? $data->currency : 'PHP'; $expense->frequency = $data->frequency; $expense->category_id = $data->category_id; $expense->start_date = $data->start_date; $expense->end_date = $data->end_date; $expense->status = !empty($data->status) ? $data->status : 'Pending'; $expense->payment_reference = $data->payment_reference; $expense->bir_2307_issuer = $data->bir_2307_issuer; $expense->bir_2307_quarter = $data->bir_2307_quarter; $expense->notes = $data->notes; $expense->tax_notes = $data->tax_notes; $expense->created_by = $_SESSION['id']; // Create expense if($expense->create()) { // Set response code - 201 created http_response_code(201); echo json_encode(array("message" => "Expense was created.")); } else { // Set response code - 503 service unavailable http_response_code(503); echo json_encode(array("message" => "Unable to create expense.")); } } else { // Set response code - 400 bad request http_response_code(400); echo json_encode(array("message" => "Unable to create expense. Data is incomplete.")); } ?>
<?php // Headers header("Access-Control-Allow-Origin: *"); header("Content-Type: application/json; charset=UTF-8"); // Include required files include_once '../../config/db.php'; include_once '../../models/receivable.php'; include_once '../../config/auth.php'; // Check if user is logged in if(!is_logged_in()) { http_response_code(401); echo json_encode(array("message" => "Unauthorized. Please log in.")); exit; } // Get database connection $database = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); // Initialize receivable object $receivable = new Receivable($database); // Get query parameters $status = isset($_GET['status']) ? $_GET['status'] : null; $client_id = isset($_GET['client_id']) ? $_GET['client_id'] : null; $project_id = isset($_GET['project_id']) ? $_GET['project_id'] : null; $bir_2307 = isset($_GET['bir_2307']) ? $_GET['bir_2307'] : null; $quarter = isset($_GET['quarter']) ? $_GET['quarter'] : null; $year = isset($_GET['year']) ? $_GET['year'] : date('Y'); // Get receivables $result = $receivable->read($status, $client_id, $project_id, $bir_2307, $quarter, $year); // Check if any receivables found if($result->num_rows > 0) { // Receivables array $receivables_arr = array(); $receivables_arr["records"] = array(); // Retrieve table contents while($row = $result->fetch_assoc()) { extract($row); $receivable_item = array( "id" => $id, "project_id" => $project_id, "project_name" => $project_name, "client_id" => $client_id, "client_name" => $client_name, "invoice_number" => $invoice_number, "service_or_number" => $service_or_number, "amount_before_tax" => $amount_before_tax, "tax_amount" => $tax_amount, "total_amount" => $total_amount, "currency" => $currency, "billing_sent" => $billing_sent, "billing_date" => $billing_date, "payment_status" => $payment_status, "payment_date" => $payment_date, "bank_deposited" => $bank_deposited, "payment_reference" => $payment_reference, "bir_2307_received" => $bir_2307_received, "bir_2307_issuer" => $bir_2307_issuer, "bir_2307_quarter" => $bir_2307_quarter, "check_image" => $check_image, "due_date" => $due_date, "notes" => $notes, "tax_notes" => $tax_notes, "created_at" => $created_at ); // Push to "records" array_push($receivables_arr["records"], $receivable_item); } // Set response code - 200 OK http_response_code(200); // Show receivables data in JSON format echo json_encode($receivables_arr); } else { // Set response code - 404 Not found http_response_code(404); // Tell the user no receivables found echo json_encode(array("message" => "No receivables found.")); } ?>
The FinanceTrack application includes several security features to protect your financial data:
Passwords are hashed using PHP's password_hash() function with bcrypt algorithm.
Different user roles (superadmin, admin, manager, accountant, viewer) have specific permissions.
All database queries use prepared statements with parameter binding.
User inputs are properly escaped when output in HTML using htmlspecialchars().
All user actions are logged with timestamps and IP addresses for audit trails.
Secure session handling with regeneration on privilege changes and automatic timeout.
Secure password reset mechanism with time-limited tokens and email verification.
Strict file type validation and randomized file names to prevent unauthorized file execution.
You can customize various aspects of the FinanceTrack application to match your specific business needs:
assets/img/logo.png
with your own logoassets/css/style.css
config/config.php
by changing APP_NAME
templates
folderconfig/config.php
config/config.php
reports
folderusers
table and updating the role checksTo add custom fields to expenses or receivables:
-- 1. Add column to database table ALTER TABLE expenses ADD COLUMN vendor_id INT; -- 2. Update model file (models/expense.php) // Add to property declarations public $vendor_id; // Add to create method $sql = "INSERT INTO expenses (name, amount_before_tax, tax_amount, total_amount, currency, frequency, category_id, start_date, end_date, status, payment_reference, bir_2307_issuer, bir_2307_quarter, notes, tax_notes, vendor_id, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; $stmt->bind_param("sdddssisssssssii", $this->name, $this->amount_before_tax, $this->tax_amount, $this->total_amount, $this->currency, $this->frequency, $this->category_id, $this->start_date, $this->end_date, $this->status, $this->payment_reference, $this->bir_2307_issuer, $this->bir_2307_quarter, $this->notes, $this->tax_notes, $this->vendor_id, // Add the new field $this->created_by ); // 3. Update the form in HTML // Add to expense form <div class="form-group"> <label for="vendor_id">Vendor</label> <select class="form-control" id="vendor_id" name="vendor_id"> <option value="">Select Vendor</option> <?php // Get all vendors $result = $database->query("SELECT id, name FROM vendors ORDER BY name"); while($row = $result->fetch_assoc()) { echo "<option value='" . $row['id'] . "'>" . htmlspecialchars($row['name']) . "</option>"; } ?> </select> </div> // 4. Update API endpoint (api/expenses/create.php) // Add to expense properties $expense->vendor_id = $data->vendor_id;
If you encounter database connection errors:
config/db.php
are correctIf users can't upload check images or attachments:
uploads
directory (should be 755 or 777)file_uploads = On
)# In .htaccess php_value upload_max_filesize 10M php_value post_max_size 12M php_value max_execution_time 300 php_value max_input_time 300
If you encounter PHP errors or warnings:
// Add to top of index.php ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL);
If users can't log in or sessions expire quickly:
If interactive features aren't working:
If you continue to experience problems: