FinanceTrack

Complete Source Code Package

A comprehensive PHP-based financial management system with expense tracking, accounts receivables, and detailed financial reporting for Philippine businesses.

Overview

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.

Complete Source Code

All PHP, JavaScript, CSS, and HTML files

Download Package (.zip)

Database Schema

SQL file with complete database structure

Download Database (.sql)

Key Features

Expense Management

  • Track recurring and one-time expenses
  • Categorize expenses (Marketing, Operations, etc.)
  • Track payment status and references
  • PHP currency as default with support for USD
  • Detailed tax tracking for expenses

Accounts Receivable

  • Client and project management
  • Invoice tracking and payment status
  • Track billing sent status
  • Support for staggered payments
  • Check image attachment for payments

BIR Form 2307 Tracking

  • Track 2307 forms issued by clients
  • Track 2307 forms issued to suppliers
  • Quarterly 2307 categorization
  • Missing 2307 reports
  • Correlation between Service Invoices and 2307s

Financial Dashboard

  • Visual charts of monthly finances
  • Quarterly goal tracking
  • Annual expense prediction
  • Cash flow visualization
  • Financial goal progress tracking

User Management

  • Role-based access control
  • Super admin password override
  • Secure authentication system
  • Audit trail for user actions
  • Customizable user permissions

Responsive Design

  • Works on desktop, tablet, and mobile
  • Left-side hierarchical navigation
  • Clean, professional interface
  • ChimesConsulting-inspired design
  • Interactive and user-friendly UI

System Requirements

  • Web Server

    Apache 2.4+ or Nginx (with PHP support)

  • PHP Version

    PHP 8.1 or higher (compatible with Namecheap Stellar hosting)

  • Database

    MySQL 5.7+ or MariaDB 10.3+

  • PHP Extensions

    mysqli, json, fileinfo, gd, session, mbstring

  • Disk Space

    Minimum 50MB for application files (more needed for attachments)

  • PHP Settings

    upload_max_filesize: 10M+ (for check image uploads)

    post_max_size: 12M+

    memory_limit: 128M+

Installation Guide

Step 1: Set Up Database

  1. Log in to your cPanel account
  2. Find and click on "MySQL Databases"
  3. Create a new database named "finance_tracker" (or your preferred name)
  4. Create a new database user with a secure password
  5. Add the user to the database with "ALL PRIVILEGES"
  6. Note your database name, username, and password for later use

Step 2: Upload Files

  1. Download the FinanceTrack package from the link above
  2. Extract the ZIP file on your local computer
  3. Log in to your cPanel account
  4. Navigate to "File Manager" and enter your public_html directory (or desired subdirectory)
  5. Click "Upload" and select all extracted files
  6. Alternatively, use FTP software like FileZilla to upload files

Step 3: Import Database Schema

  1. In cPanel, click on "phpMyAdmin"
  2. Select the database you created in Step 1
  3. Click on the "Import" tab
  4. Click "Choose File" and select the database.sql file from the package
  5. Click "Go" to import the database structure and demo data

Step 4: Configure Database Connection

  1. Navigate to the config folder in the uploaded files
  2. Find the db.php file
  3. Edit this file with the File Manager or download, edit locally, and re-upload
  4. Update the database connection details with your information:
    <?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);
    }
    ?>

Step 5: Set File Permissions

  1. In File Manager, select all directories
  2. Click "Permissions" (or right-click and select "Change Permissions")
  3. Set directory permissions to 755 (rwxr-xr-x)
  4. For the uploads folder specifically, set permissions to 777 temporarily if needed for file uploads
  5. For all files, set permissions to 644 (rw-r--r--)

Step 6: Access Your Installation

  1. Open your web browser and navigate to your domain (e.g., https://yourdomain.com/)
  2. You should see the FinanceTrack login page
  3. Use the default login credentials:
    • Username: superadmin
    • Password: admin123
  4. After successful login, immediately change the default password

Step 7: Configure PHP Settings (If Needed)

  1. In cPanel, click on "PHP Version"
  2. Ensure PHP 8.1 is selected
  3. Click on "Switch to PHP Options"
  4. Adjust settings as needed:
    • upload_max_filesize: 10M or higher
    • post_max_size: Larger than upload_max_filesize
    • max_execution_time: 60 or higher
    • memory_limit: 128M or higher
  5. Click "Save" to apply your PHP configuration changes

Database Schema

Below 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

File Structure

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

Configuration Files

The key configuration files that need to be modified for your installation. These files control database connections, application settings, and authentication.

config/db.php

<?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);
}
?>

config/config.php

<?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');
?>

config/auth.php

<?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();
    }
}
?>

Core PHP Files

These are the main PHP files that drive the application. They handle user authentication, dashboard display, and core functionality.

login.php

<?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>

index.php (Dashboard)

<?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>

API Endpoints

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.

Example: api/expenses/create.php

<?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."));
}
?>

Example: api/receivables/read.php

<?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."));
}
?>

Security Features

The FinanceTrack application includes several security features to protect your financial data:

  • Secure Authentication

    Passwords are hashed using PHP's password_hash() function with bcrypt algorithm.

  • Role-Based Access Control

    Different user roles (superadmin, admin, manager, accountant, viewer) have specific permissions.

  • SQL Injection Prevention

    All database queries use prepared statements with parameter binding.

  • XSS Protection

    User inputs are properly escaped when output in HTML using htmlspecialchars().

  • Activity Logging

    All user actions are logged with timestamps and IP addresses for audit trails.

  • Session Management

    Secure session handling with regeneration on privilege changes and automatic timeout.

  • Password Reset Security

    Secure password reset mechanism with time-limited tokens and email verification.

  • File Upload Protection

    Strict file type validation and randomized file names to prevent unauthorized file execution.

Customization

You can customize various aspects of the FinanceTrack application to match your specific business needs:

1. Branding and Appearance

  • Update logo by replacing assets/img/logo.png with your own logo
  • Modify the color scheme in assets/css/style.css
  • Update company name in config/config.php by changing APP_NAME
  • Customize email templates in the templates folder

2. Business Settings

  • Add or modify expense categories in the database or through the admin interface
  • Update default currency in config/config.php
  • Modify date and time formats in config/config.php

3. Extending Functionality

  • Add new reports by creating new files in the reports folder
  • Extend user roles by modifying the users table and updating the role checks
  • Add new API endpoints for additional functionality

4. Custom Fields

To add custom fields to expenses or receivables:

  1. Add new columns to the respective database table
  2. Update the corresponding model file to include the new fields
  3. Modify the form templates to include input fields for the new data
  4. Update the API endpoints to handle the new fields

Example: Adding a new field to expenses

-- 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;

Troubleshooting

Database Connection Issues

If you encounter database connection errors:

  • Verify that your database credentials in config/db.php are correct
  • Ensure that the MySQL server is running
  • Check if your hosting provider requires a specific database host (not always 'localhost')
  • Verify that the database user has sufficient privileges

File Upload Problems

If users can't upload check images or attachments:

  • Check the permissions on the uploads directory (should be 755 or 777)
  • Verify that PHP settings allow for file uploads (file_uploads = On)
  • Increase PHP limits in php.ini or .htaccess:
    # 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
  • Check for any file type restrictions in your hosting environment

PHP Version Compatibility

If you encounter PHP errors or warnings:

  • Verify that your server is running PHP 8.1 or higher
  • Check for deprecated functions that might have been removed in PHP 8.1
  • Enable error reporting temporarily to see detailed errors:
    // Add to top of index.php
    ini_set('display_errors', 1);
    ini_set('display_startup_errors', 1);
    error_reporting(E_ALL);
  • Check PHP error logs in your hosting control panel

Login or Session Issues

If users can't log in or sessions expire quickly:

  • Check PHP session settings in php.ini:
  • Ensure session.save_path is writable
  • Verify that session.cookie_lifetime and session.gc_maxlifetime are set appropriately
  • Check if your hosting has session-related restrictions
  • Try using a different session storage method if necessary

JavaScript and AJAX Issues

If interactive features aren't working:

  • Check browser console for JavaScript errors
  • Verify that all JavaScript files are being loaded correctly
  • Ensure API endpoints are accessible and returning proper JSON responses
  • Check for cross-origin request issues (CORS)
  • Test with different browsers to isolate browser-specific issues

Still Having Issues?

If you continue to experience problems:

  • Check the PHP and MySQL versions installed on your server
  • Verify all required PHP extensions are installed
  • Try reinstalling the application with fresh files
  • Contact your hosting provider for assistance with server configurations
  • Reach out to the developer for support