when my config.php as below <?php
// Define the base path
define('BASE_PATH', dirname(__FILE__));

// Database configuration
define('DB_HOST', getenv('DB_HOST') ?: 'localhost');
define('DB_USER', getenv('DB_USER') ?: 'root');
define('DB_PASS', getenv('DB_PASS') ?: '');
define('DB_NAME', getenv('DB_NAME') ?: 'FinancialApp');

// Site configuration
define('SITE_URL', (isset($_SERVER['HTTPS']) ? "https" : "http") . "://localhost/invoice_system");
define('SITE_NAME', 'TABS-Invoice Management');
define('COMPANY_NAME', 'A1 Tax Assist');
define('COMPANY_ADDRESS', 'No 52 Kodigehalli Bangalore');
define('COMPANY_PHONE', '8123030275');
define('COMPANY_EMAIL', 'admin@einvoicebooks.com');

// File paths
define('LOG_PATH', BASE_PATH . '/logs');
define('UPLOAD_PATH', BASE_PATH . '/uploads');

// Create necessary directories
$directories = [LOG_PATH, UPLOAD_PATH];
foreach ($directories as $dir) {
    if (!is_dir($dir)) {
        mkdir($dir, 0755, true);
    }
}

// Error handling
error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('log_errors', 1);
ini_set('error_log', LOG_PATH . '/error.log');

// Session configuration
session_start();

// Regenerate session ID to prevent session fixation
session_regenerate_id(true);

// Helper functions
function formatCurrency($amount, $symbol = '₹') {
    return $symbol . number_format((float)$amount, 2, '.', ',');
}

function sanitizeInput($data) {
    return htmlspecialchars(strip_tags(trim($data)));
}

function logError($message) {
    error_log(date('[Y-m-d H:i:s] ') . $message . PHP_EOL, 3, LOG_PATH . '/error.log');
}

function getDbConnection() {
    $conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
    if ($conn->connect_error) {
        logError("Connection failed: " . $conn->connect_error);
        die("Database connection error.");
    }
    return $conn;
}

?> and database.php as below <?php
require_once 'config.php';

class Database {
    private $connection = null;

    public function __construct() {
        $this->connect();
    }
    
    // Database connection method
    private function connect() {
        try {
            $this->connection = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);

            if ($this->connection->connect_error) {
                throw new Exception("Connection failed: " . $this->connection->connect_error);
            }

            // Set charset to utf8mb4 for better character support
            $this->connection->set_charset("utf8mb4");
        } catch (Exception $e) {
            logError($e->getMessage());
            die("Database connection failed. Please check the error log.");
        }
    }

    // Prepare a statement for execution
    public function prepare($sql) {
        return $this->connection->prepare($sql);
    }

    // Execute a basic query
    public function query($sql) {
        try {
            $result = $this->connection->query($sql);
            if ($result === false) {
                throw new Exception($this->connection->error);
            }
            return $result;
        } catch (Exception $e) {
            logError("Query Error: " . $e->getMessage() . " | SQL: " . $sql);
            throw $e;
        }
    }

    // Get single row
    public function getRow($sql) {
        try {
            $result = $this->query($sql);
            return $result->fetch_assoc(); // return associative array
        } catch (Exception $e) {
            logError("getRow Error: " . $e->getMessage());
            return false;
        }
    }

    // Get multiple rows
    public function getRows($sql) {
        try {
            $result = $this->query($sql);
            return $result->fetch_all(MYSQLI_ASSOC); // return an array of associative arrays
        } catch (Exception $e) {
            logError("getRows Error: " . $e->getMessage());
            return [];
        }
    }

    // Insert record
    public function insert($table, $data) {
        try {
            // Prepare fields and values for SQL insert
            $fields = implode(', ', array_keys($data));
            $placeholders = implode(', ', array_fill(0, count($data), '?'));
            $sql = "INSERT INTO $table ($fields) VALUES ($placeholders)";
            
            $stmt = $this->connection->prepare($sql);
            if ($stmt === false) {
                throw new Exception("Preparation failed: " . $this->connection->error);
            }

            // Determine the parameter types (assumes all data is string for simplicity)
            $types = str_repeat('s', count($data));
            $stmt->bind_param($types, ...array_values($data));

            if ($stmt->execute()) {
                return $this->connection->insert_id; // return the last inserted ID
            } else {
                throw new Exception("Execution failed: " . $stmt->error);
            }
        } catch (Exception $e) {
            logError("Insert Error: " . $e->getMessage());
            return false;
        }
    }

    // Update record
    public function update($table, $data, $where) {
        try {
            $sets = [];
            foreach ($data as $field => $value) {
                $sets[] = "$field = ?";
            }
            $sql = "UPDATE $table SET " . implode(', ', $sets) . " WHERE $where";
            
            $stmt = $this->connection->prepare($sql);
            if ($stmt === false) {
                throw new Exception("Preparation failed: " . $this->connection->error);
            }

            // Prepare the parameter array
            $types = str_repeat('s', count($data));
            $stmt->bind_param($types, ...array_values($data));

            return $stmt->execute();
        } catch (Exception $e) {
            logError("Update Error: " . $e->getMessage());
            return false;
        }
    }

    // Delete record
    public function delete($table, $where) {
        try {
            $sql = "DELETE FROM $table WHERE $where";
            return $this->query($sql);
        } catch (Exception $e) {
            logError("Delete Error: " . $e->getMessage());
            return false;
        }
    }

    // Escape string
    public function escape($value) {
        return $this->connection->real_escape_string($value);
    }

    // Close connection
    public function __destruct() {
        if ($this->connection) {
            $this->connection->close();
        }
    }
}

// Usage of the Database class
try {
    $db = new Database();
} catch (Exception $e) {
    die("Failed to initialize database connection.");
}
?> and database as below -- Create database
CREATE DATABASE IF NOT EXISTS FinancialApp;
USE FinancialApp;

-- --------------------
-- Table Creation
-- --------------------

-- Users Table
CREATE TABLE IF NOT EXISTS users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    email VARCHAR(100) NOT NULL UNIQUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Customers Table
CREATE TABLE IF NOT EXISTS customers (
    id INT PRIMARY KEY AUTO_INCREMENT,
    customer_name VARCHAR(100) NOT NULL,
    email VARCHAR(100),
    phone VARCHAR(20),
    address TEXT,
    city VARCHAR(50),
    state VARCHAR(50),
    zip_code VARCHAR(10),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Products Table
CREATE TABLE IF NOT EXISTS products (
    id INT PRIMARY KEY AUTO_INCREMENT,
    product_name VARCHAR(100) NOT NULL,
    description TEXT,
    unit_price DECIMAL(10, 2) NOT NULL,
    stock_quantity INT NOT NULL DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Invoices Table
CREATE TABLE IF NOT EXISTS invoices (
    id INT PRIMARY KEY AUTO_INCREMENT,
    invoice_number VARCHAR(20) NOT NULL UNIQUE,
    customer_id INT NOT NULL,
    invoice_date DATE NOT NULL,
    due_date DATE NOT NULL,
    subtotal DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
    tax_rate DECIMAL(5, 2) DEFAULT 0.00,
    tax_amount DECIMAL(10, 2) DEFAULT 0.00,
    total_amount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
    notes TEXT,
    status ENUM('pending', 'paid', 'cancelled') DEFAULT 'pending',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (customer_id) REFERENCES customers(id)
);

-- Invoice Items Table
CREATE TABLE IF NOT EXISTS invoice_items (
    id INT PRIMARY KEY AUTO_INCREMENT,
    invoice_id INT NOT NULL,
    product_id INT NOT NULL,
    quantity INT NOT NULL,
    unit_price DECIMAL(10, 2) NOT NULL,
    total_price DECIMAL(10, 2) NOT NULL,
    FOREIGN KEY (invoice_id) REFERENCES invoices(id),
    FOREIGN KEY (product_id) REFERENCES products(id)
);

-- Payments Table
CREATE TABLE IF NOT EXISTS payments (
    id INT PRIMARY KEY AUTO_INCREMENT,
    invoice_id INT NOT NULL,
    amount DECIMAL(10, 2) NOT NULL,
    payment_date DATE NOT NULL,
    payment_method VARCHAR(50) NOT NULL,
    reference_number VARCHAR(100),
    notes TEXT,
    status ENUM('pending', 'completed', 'failed') DEFAULT 'completed',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (invoice_id) REFERENCES invoices(id)
);

-- Recurring Invoices Table
CREATE TABLE IF NOT EXISTS recurring_invoices (
    id INT PRIMARY KEY AUTO_INCREMENT,
    customer_id INT NOT NULL,
    frequency ENUM('weekly', 'monthly', 'quarterly', 'yearly') NOT NULL,
    start_date DATE NOT NULL,
    end_date DATE,
    next_date DATE NOT NULL,
    total_amount DECIMAL(10, 2) NOT NULL,
    notes TEXT,
    status ENUM('active', 'paused', 'completed') DEFAULT 'active',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (customer_id) REFERENCES customers(id)
);

-- Recurring Items Table
CREATE TABLE IF NOT EXISTS recurring_items (
    id INT PRIMARY KEY AUTO_INCREMENT,
    recurring_id INT NOT NULL,
    product_id INT NOT NULL,
    quantity INT NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    FOREIGN KEY (recurring_id) REFERENCES recurring_invoices(id),
    FOREIGN KEY (product_id) REFERENCES products(id)
);

-- Invoice Templates Table
CREATE TABLE IF NOT EXISTS invoice_templates (
    id INT PRIMARY KEY AUTO_INCREMENT,
    template_name VARCHAR(100) NOT NULL,
    customer_type ENUM('all', 'corporate', 'individual') NOT NULL DEFAULT 'all',
    description TEXT,
    payment_terms INT DEFAULT 30,
    tax_rate DECIMAL(5, 2) DEFAULT 0.00,
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Template Items Table
CREATE TABLE IF NOT EXISTS template_items (
    id INT PRIMARY KEY AUTO_INCREMENT,
    template_id INT NOT NULL,
    product_id INT NOT NULL,
    quantity INT NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    FOREIGN KEY (template_id) REFERENCES invoice_templates(id),
    FOREIGN KEY (product_id) REFERENCES products(id)
);

-- --------------------
-- Views Creation
-- --------------------

CREATE VIEW vw_sales_summary AS
SELECT 
    COUNT(i.id) as total_invoices,
    SUM(i.total_amount) as total_revenue,
    AVG(i.total_amount) as avg_invoice_value
FROM invoices i
WHERE i.status = 'paid';

CREATE VIEW vw_customer_summary AS
SELECT 
    c.id,
    c.customer_name,
    COUNT(i.id) as invoice_count,
    SUM(i.total_amount) as total_sales,
    AVG(i.total_amount) as avg_invoice_value,
    MAX(i.invoice_date) as last_invoice_date
FROM customers c
LEFT JOIN invoices i ON c.id = i.customer_id
GROUP BY c.id;

CREATE VIEW vw_product_performance AS
SELECT 
    p.id,
    p.product_name,
    SUM(ii.quantity) as units_sold,
    SUM(ii.unit_price * ii.quantity) as total_sold,
    COUNT(DISTINCT i.id) as invoice_count
FROM products p
LEFT JOIN invoice_items ii ON p.id = ii.product_id
LEFT JOIN invoices i ON ii.invoice_id = i.id
GROUP BY p.id;

-- --------------------
-- Procedures Creation
-- --------------------

DELIMITER //

CREATE PROCEDURE sp_create_invoice_from_recurring (
    IN p_recurring_id INT
)
BEGIN
    -- Logic to generate an invoice from a recurring invoice process.
    DECLARE v_customer_id INT;
    DECLARE v_total_amount DECIMAL(10,2);
    
    SELECT customer_id, total_amount INTO v_customer_id, v_total_amount 
    FROM recurring_invoices WHERE id = p_recurring_id;
    
    INSERT INTO invoices (
        invoice_number, customer_id, invoice_date, due_date,
        subtotal, total_amount
    ) VALUES (
        CONCAT('INV', DATE_FORMAT(NOW(), '%Y%m%d'), '-', p_recurring_id),
        v_customer_id,
        CURDATE(),
        DATE_ADD(CURDATE(), INTERVAL 30 DAY),
        v_total_amount,
        v_total_amount
    );
END //

CREATE PROCEDURE sp_update_recurring_invoices()
BEGIN
    -- Logic to update the next date for active recurring invoices.
    UPDATE recurring_invoices 
    SET next_date = CASE frequency
        WHEN 'weekly' THEN DATE_ADD(next_date, INTERVAL 7 DAY)
        WHEN 'monthly' THEN DATE_ADD(next_date, INTERVAL 1 MONTH)
        WHEN 'quarterly' THEN DATE_ADD(next_date, INTERVAL 3 MONTH)
        WHEN 'yearly' THEN DATE_ADD(next_date, INTERVAL 1 YEAR)
    END
    WHERE status = 'active' AND next_date <= CURDATE();
END //

DELIMITER ;

-- ====================
-- Initial Setup Data (Optional)
-- ====================

INSERT INTO users (username, password, email) VALUES
('admin', '$2y$10$hashedpassword', 'admin@example.com'); -- Change this password hash to a real hash and api.php as below <?php
require_once '../../config/config.php';
require_once '../../config/database.php';

header('Content-Type: application/json');

$action = $_POST['action'] ?? $_GET['action'] ?? '';

switch ($action) {
    case 'listCustomers':
        listCustomers($db);
        break;
    case 'addCustomer':
        addCustomer($db);
        break;
    case 'editCustomer':
        editCustomer($db);
        break;
    case 'getCustomer':
        getCustomerDetails($db);
        break;
    default:
        echo json_encode(['success' => false, 'message' => 'Invalid action']);
        break;
}

// Function to list customers based on search criteria
function listCustomers($db) {
    $search = $db->real_escape_string($_GET['search'] ?? '');
    $query = "SELECT c.*, 
              (SELECT COALESCE(SUM(i.total_amount), 0) FROM invoices i WHERE i.customer_id = c.id) AS total_sales
              FROM customers c 
              WHERE (c.customer_name LIKE '%$search%' OR c.email LIKE '%$search%' OR c.phone LIKE '%$search%')";

    $result = $db->query($query);
    
    if ($result) {
        $customers = $result->fetch_all(MYSQLI_ASSOC);
        echo json_encode(['success' => true, 'data' => $customers]);
    } else {
        echo json_encode(['success' => false, 'message' => 'Failed to fetch customers']);
    }
}

// Function to add a new customer
function addCustomer($db) {
    $data = $_POST;

    // Validate and sanitize input
    $customerName = $db->real_escape_string($data['customer_name']);
    $email = $db->real_escape_string($data['email']);
    $phone = $db->real_escape_string($data['phone']);
    $company = $db->real_escape_string($data['company']);
    $address = $db->real_escape_string($data['address']);
    $city = $db->real_escape_string($data['city']);
    $state = $db->real_escape_string($data['state']);
    $zipCode = $db->real_escape_string($data['zip_code']);
    $notes = $db->real_escape_string($data['notes']);

    $query = "INSERT INTO customers (customer_name, email, phone, company, address, city, state, zip_code, notes) 
              VALUES ('$customerName', '$email', '$phone', '$company', '$address', '$city', '$state', '$zipCode', '$notes')";
    
    if ($db->query($query)) {
        echo json_encode(['success' => true, 'message' => 'Customer added successfully']);
    } else {
        echo json_encode(['success' => false, 'message' => 'Failed to add customer']);
    }
}

// Function to edit an existing customer
function editCustomer($db) {
    $data = $_POST;
    $customerId = intval($data['customer_id']); // Ensure customer ID is an integer

    // Validate and sanitize input
    $customerName = $db->real_escape_string($data['customer_name']);
    $email = $db->real_escape_string($data['email']);
    $phone = $db->real_escape_string($data['phone']);
    $company = $db->real_escape_string($data['company']);
    $address = $db->real_escape_string($data['address']);
    $city = $db->real_escape_string($data['city']);
    $state = $db->real_escape_string($data['state']);
    $zipCode = $db->real_escape_string($data['zip_code']);
    $notes = $db->real_escape_string($data['notes']);

    // Update the customer details in the database
    $query = "UPDATE customers SET 
              customer_name = '$customerName', 
              email = '$email', 
              phone = '$phone', 
              company = '$company', 
              address = '$address', 
              city = '$city', 
              state = '$state', 
              zip_code = '$zipCode', 
              notes = '$notes' 
              WHERE id = $customerId";
    
    if ($db->query($query)) {
        echo json_encode(['success' => true, 'message' => 'Customer updated successfully']);
    } else {
        echo json_encode(['success' => false, 'message' => 'Failed to update customer']);
    }
}

// Function to get details of a specific customer
function getCustomerDetails($db) {
    $customerId = intval($_GET['id']);

    $query = "SELECT c.*, 
              (SELECT COALESCE(SUM(i.total_amount), 0) FROM invoices i WHERE i.customer_id = c.id) AS total_sales
              FROM customers c 
              WHERE c.id = ?";

    $stmt = $db->prepare($query);
    $stmt->bind_param('i', $customerId);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($customer = $result->fetch_assoc()) {
        echo json_encode(['success' => true, 'data' => $customer]);
    } else {
        echo json_encode(['success' => false, 'message' => 'Failed to fetch customer details.']);
    }
}
?>
below index php not loading data from database <?php
require_once '../../config/config.php';
require_once '../../config/database.php';
require_once '../../includes/header.php';
?>


<div class="container">
    <h2 class="mt-4">Customer Management</h2>

    <!-- Button to Open Add Customer Modal -->
    <button type="button" class="btn btn-primary mb-3" data-bs-toggle="modal" data-bs-target="#addCustomerModal">
        <i class="fas fa-plus"></i> Add New Customer
    </button>
    
    <!-- Customers Table -->
    <table id="customersTable" class="table table-striped">
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Email</th>
                <th>Phone</th>
                <th>Total Sales</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody>
            <!-- Dynamic content will be loaded here by JavaScript -->
        </tbody>
    </table>

    <!-- Add Customer Modal -->
    <div class="modal fade" id="addCustomerModal" tabindex="-1" aria-labelledby="addCustomerModalLabel" aria-hidden="true">
        <div class="modal-dialog modal-lg">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="addCustomerModalLabel">Add New Customer</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <form id="addCustomerForm">
                    <div class="modal-body">
                        <div class="mb-3">
                            <label for="customer_name" class="form-label">Customer Name *</label>
                            <input type="text" class="form-control" name="customer_name" required>
                        </div>
                        <div class="mb-3">
                            <label for="email" class="form-label">Email *</label>
                            <input type="email" class="form-control" name="email" required>
                        </div>
                        <div class="mb-3">
                            <label for="phone" class="form-label">Phone</label>
                            <input type="tel" class="form-control" name="phone">
                        </div>
                        <div class="mb-3">
                            <label for="company" class="form-label">Company</label>
                            <input type="text" class="form-control" name="company">
                        </div>
                        <div class="mb-3">
                            <label for="address" class="form-label">Address</label>
                            <textarea class="form-control" name="address" rows="2"></textarea>
                        </div>
                        <div class="mb-3">
                            <label for="city" class="form-label">City</label>
                            <input type="text" class="form-control" name="city">
                        </div>
                        <div class="mb-3">
                            <label for="state" class="form-label">State</label>
                            <input type="text" class="form-control" name="state">
                        </div>
                        <div class="mb-3">
                            <label for="zip_code" class="form-label">ZIP Code</label>
                            <input type="text" class="form-control" name="zip_code">
                        </div>
                        <div class="mb-3">
                            <label for="notes" class="form-label">Notes</label>
                            <textarea class="form-control" name="notes" rows="3"></textarea>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                        <button type="submit" class="btn btn-primary">Save Customer</button>
                    </div>
                </form>
            </div>
        </div>
    </div>

    <!-- Edit Customer Modal -->
    <div class="modal fade" id="editCustomerModal" tabindex="-1" aria-labelledby="editCustomerModalLabel" aria-hidden="true">
        <div class="modal-dialog modal-lg">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="editCustomerModalLabel">Edit Customer</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <form id="editCustomerForm">
                    <input type="hidden" name="customer_id">
                    <div class="modal-body">
                        <div class="mb-3">
                            <label for="edit_customer_name" class="form-label">Customer Name *</label>
                            <input type="text" class="form-control" name="customer_name" required>
                        </div>
                        <div class="mb-3">
                            <label for="edit_email" class="form-label">Email *</label>
                            <input type="email" class="form-control" name="email" required>
                        </div>
                        <div class="mb-3">
                            <label for="edit_phone" class="form-label">Phone</label>
                            <input type="tel" class="form-control" name="phone">
                        </div>
                        <div class="mb-3">
                            <label for="edit_company" class="form-label">Company</label>
                            <input type="text" class="form-control" name="company">
                        </div>
                        <div class="mb-3">
                            <label for="edit_address" class="form-label">Address</label>
                            <textarea class="form-control" name="address" rows="2"></textarea>
                        </div>
                        <div class="mb-3">
                            <label for="edit_city" class="form-label">City</label>
                            <input type="text" class="form-control" name="city">
                        </div>
                        <div class="mb-3">
                            <label for="edit_state" class="form-label">State</label>
                            <input type="text" class="form-control" name="state">
                        </div>
                        <div class="mb-3">
                            <label for="edit_zip_code" class="form-label">ZIP Code</label>
                            <input type="text" class="form-control" name="zip_code">
                        </div>
                        <div class="mb-3">
                            <label for="edit_notes" class="form-label">Notes</label>
                            <textarea class="form-control" name="notes" rows="3"></textarea>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                        <button type="submit" class="btn btn-primary">Update Customer</button>
                    </div>
                </form>
            </div>
        </div>
    </div>

</div>

<script src="assets/js/customers.js"></script>

<?php require_once '../../includes/footer.php'; ?>