How to create Expense Management System PHP and MySQL

Today, We will learn how to create an expense management system in PHP and MySQL. Read: How to Live Search using PHP MySQL and Ajax

Expense management systems are web-based applications to manage their income and expenses. The users are allowed to log in system and manage their income and expenses and view the report of expenses for a range of times. This is an initial level project in which we have covered sections like manage income, expenses, users, etc.

Read more: How to Create Simple REST API PHP and MySQL

Step1: Create MySQL Database Table for Expense Management System

We will create a table with the name of "expense_users" to store user login information.

CREATE TABLE `expense_users` (
  `id` int(11) UNSIGNED NOT NULL,
  `first_name` varchar(255) DEFAULT NULL,
  `last_name` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `password` varchar(64) NOT NULL,
  `role` enum('admin') DEFAULT 'admin'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `expense_users`
  ADD PRIMARY KEY (`id`);

ALTER TABLE `expense_users`
  MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;

we will create a database table “expense_income_category” to store income category details.

CREATE TABLE `expense_income_category` (
  `id` int(11) NOT NULL,
  `name` varchar(250) NOT NULL,
  `status` enum('enable','disable') NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE `expense_income_category`
  ADD PRIMARY KEY (`id`);
  
ALTER TABLE `expense_income_category`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;

we will create a table with the name of "expense_income" to store income details.

CREATE TABLE `expense_income` (
  `id` int(11) NOT NULL,
  `amount` int(11) NOT NULL,
  `date` date NOT NULL,
  `category_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE `expense_income`
  ADD PRIMARY KEY (`id`);
  
ALTER TABLE `expense_income`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;

we will create a table “expense_category" to store expense category details.

CREATE TABLE `expense_category` (
  `id` int(11) NOT NULL,
  `name` varchar(250) NOT NULL,
  `status` enum('enable','disable') NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE `expense_category`
  ADD PRIMARY KEY (`id`);
  
ALTER TABLE `expense_category`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8;

and we will create a table with the name of "expense_expense" to store expense details.

CREATE TABLE `expense_expense` (
  `id` int(11) NOT NULL,
  `amount` int(11) NOT NULL,
  `date` date NOT NULL,
  `category_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE `expense_expense`
  ADD PRIMARY KEY (`id`);

ALTER TABLE `expense_expense`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;

Step2: Manage Income for Expense Management System

In income.php file, we will create HTML to manage income.

<div> 	
	<div class="panel-heading">
		<div class="row">
			<div class="col-md-10">
				<h3 class="panel-title"></h3>
			</div>
			<div class="col-md-2" align="right">
				<button type="button" id="addIncome" class="btn btn-info" title="Add Income"><span class="glyphicon glyphicon-plus"></span></button>
			</div>
		</div>
	</div>
	<table id="incomeListing" class="table table-bordered table-striped">
		<thead>
			<tr>						
				<th>Sn.</th>					
				<th>Amount</th>					
				<th>Category</th>
				<th>Date</th>						
				<th></th>
				<th></th>					
			</tr>
		</thead>
	</table>
</div>

we will make an ajax request to income_action.php with action listIncome to load incode data in Datatable.

var incomeRecords = $('#incomeListing').DataTable({
	"lengthChange": false,
	"processing":true,
	"serverSide":true,		
	"bFilter": false,
	'serverMethod': 'post',		
	"order":[],
	"ajax":{
		url:"income_action.php",
		type:"POST",
		data:{action:'listIncome'},
		dataType:"json"
	},
	"columnDefs":[
		{
			"targets":[0, 4, 5],
			"orderable":false,
		},
	],
	"pageLength": 10
});	

we will check for action listIncome in income_action.php and call method listIncome() from class Income.php to load income data.

$income = new Income($db);
if(!empty($_POST['action']) && $_POST['action'] == 'listIncome') {
	$income->listIncome();
}

we will implement the method listIncome() in class Income.php and return income data as JSON data to load into a data table.

public function listIncome(){		
		
	if($_SESSION["userid"]) {
		$sqlQuery = "SELECT income.id, income.amount, income.date, category.name
			FROM ".$this->incomeTable." AS income 
			LEFT JOIN ".$this->incomeCategoryTable." AS category ON income.category_id = category.id 
			WHERE income.user_id = '".$_SESSION["userid"]."' ";		
			
		if(!empty($_POST["search"]["value"])){
			$sqlQuery .= ' AND (income.id LIKE "%'.$_POST["search"]["value"].'%" ';
			$sqlQuery .= ' OR income.amount LIKE "%'.$_POST["search"]["value"].'%" ';
			$sqlQuery .= ' OR income.date LIKE "%'.$_POST["search"]["value"].'%" ';
			$sqlQuery .= ' OR category.name LIKE "%'.$_POST["search"]["value"].'%" ';							
		}
		
		if(!empty($_POST["order"])){
			$sqlQuery .= 'ORDER BY '.$_POST['order']['0']['column'].' '.$_POST['order']['0']['dir'].' ';
		} else {
			$sqlQuery .= 'ORDER BY income.id ';
		}
		
		if($_POST["length"] != -1){
			$sqlQuery .= 'LIMIT ' . $_POST['start'] . ', ' . $_POST['length'];
		}	
		
		$stmt = $this->conn->prepare($sqlQuery);
		$stmt->execute();
		$result = $stmt->get_result();	
		
		$stmtTotal = $this->conn->prepare($sqlQuery);
		$stmtTotal->execute();
		$allResult = $stmtTotal->get_result();
		$allRecords = $allResult->num_rows;
		
		$displayRecords = $result->num_rows;
		$records = array();		
		$count = 1;
		while ($income = $result->fetch_assoc()) { 				
			$rows = array();			
			$rows[] = $count;
			$rows[] = ucfirst($income['amount']);
			$rows[] = $income['name'];	
			$rows[] = $income['date'];			
			$rows[] = '<button type="button" name="update" id="'.$income["id"].'" class="btn btn-warning btn-xs update"><span class="glyphicon glyphicon-edit" title="Edit"></span></button>';
			$rows[] = '<button type="button" name="delete" id="'.$income["id"].'" class="btn btn-danger btn-xs delete" ><span class="glyphicon glyphicon-remove" title="Delete"></span></button>';
			$records[] = $rows;
			$count++;
		}
		
		$output = array(
			"draw"	=>	intval($_POST["draw"]),			
			"iTotalRecords"	=> 	$displayRecords,
			"iTotalDisplayRecords"	=>  $allRecords,
			"data"	=> 	$records
		);
		
		echo json_encode($output);
	}
}	

Step3: Manage Expenses for Expense Management System

We will create HTML in expense.php to list expenses records.

<div> 	
	<div class="panel-heading">
		<div class="row">
			<div class="col-md-10">
				<h3 class="panel-title"></h3>
			</div>
			<div class="col-md-2" align="right">
				<button type="button" id="addExpense" class="btn btn-info" title="Add expense"><span class="glyphicon glyphicon-plus"></span></button>
			</div>
		</div>
	</div>
	<table id="expenseListing" class="table table-bordered table-striped">
		<thead>
			<tr>						
				<th>Sn.</th>					
				<th>Amount</th>					
				<th>Category</th>
				<th>Date</th>						
				<th></th>
				<th></th>					
			</tr>
		</thead>
	</table>
</div>

we will initialize data tables and make an ajax request with action listExpense to load expense records.

var expenseRecords = $('#expenseListing').DataTable({
	"lengthChange": false,
	"processing":true,
	"serverSide":true,		
	"bFilter": false,
	'serverMethod': 'post',		
	"order":[],
	"ajax":{
		url:"expense_action.php",
		type:"POST",
		data:{action:'listExpense'},
		dataType:"json"
	},
	"columnDefs":[
		{
			"targets":[0, 4, 5],
			"orderable":false,
		},
	],
	"pageLength": 10
});	

we will check for action listExpense in expense_action.php and call method listExpense() from class Expense.php.

if(!empty($_POST['action']) && $_POST['action'] == 'listExpense') {
	$expense->listExpense();
}

we will implement the method listExpense() in class Expense.php to return expenses list as JSON data.

public function listExpense(){		
	if($_SESSION["userid"]) {
		$sqlQuery = "SELECT expense.id, expense.amount, expense.date, category.name
			FROM ".$this->expenseTable." AS expense 
			LEFT JOIN ".$this->categoryTable." AS category ON expense.category_id = category.id 
			WHERE expense.user_id = '".$_SESSION["userid"]."' ";			
			
		if(!empty($_POST["search"]["value"])){
			$sqlQuery .= ' AND (expense.id LIKE "%'.$_POST["search"]["value"].'%" ';
			$sqlQuery .= ' OR expense.amount LIKE "%'.$_POST["search"]["value"].'%" ';
			$sqlQuery .= ' OR expense.date LIKE "%'.$_POST["search"]["value"].'%" ';
			$sqlQuery .= ' OR category.name LIKE "%'.$_POST["search"]["value"].'%") ';							
		}
		
		if(!empty($_POST["order"])){
			$sqlQuery .= 'ORDER BY '.$_POST['order']['0']['column'].' '.$_POST['order']['0']['dir'].' ';
		} else {
			$sqlQuery .= 'ORDER BY expense.date DESC ';
		}
		
		if($_POST["length"] != -1){
			$sqlQuery .= 'LIMIT ' . $_POST['start'] . ', ' . $_POST['length'];
		}	
		
		$stmt = $this->conn->prepare($sqlQuery);
		$stmt->execute();
		$result = $stmt->get_result();	
		
		$stmtTotal = $this->conn->prepare($sqlQuery);
		$stmtTotal->execute();
		$allResult = $stmtTotal->get_result();
		$allRecords = $allResult->num_rows;
		
		$displayRecords = $result->num_rows;
		$records = array();	
		$count = 1;
		while ($expense = $result->fetch_assoc()) { 				
			$rows = array();			
			$rows[] = $count;
			$rows[] = ucfirst($expense['amount']);
			$rows[] = $expense['name'];	
			$rows[] = $expense['date'];			
			$rows[] = '<button type="button" name="update" id="'.$expense["id"].'" class="btn btn-warning btn-xs update"><span class="glyphicon glyphicon-edit" title="Edit"></span></button>';
			$rows[] = '<button type="button" name="delete" id="'.$expense["id"].'" class="btn btn-danger btn-xs delete" ><span class="glyphicon glyphicon-remove" title="Delete"></span></button>';
			$records[] = $rows;
			$count++;
		}
		
		$output = array(
			"draw"	=>	intval($_POST["draw"]),			
			"iTotalRecords"	=> 	$displayRecords,
			"iTotalDisplayRecords"	=>  $allRecords,
			"data"	=> 	$records
		);
		
		echo json_encode($output);
	}
}

Step4: Implement Report for Expense Management System

In report.php file, we will create HTML to generate expenses report by date range.

<div> 	
	<div class="panel-heading">
		<div class="row">	
			<div>
				<h4>View Income and Expense Reports</h4>
			</div>
			<div class="col-md-2" style="padding-left:0px;">
				<input type="date" class="form-control" id="from_date" name="from_date" placeholder="From date" >
			</div>
			<div class="col-md-2" style="padding-left:0px;">
				<input type="date" class="form-control" id="to_date" name="to_date" placeholder="To date" >
			</div>
			<div class="col-md-2" style="padding-left:0px;">
				<button type="submit" id="viewReport" class="btn btn-info" title="View Report"><span class="glyphicon glyphicon-search"></span></button>
			</div>
		</div>
	</div>
	<table class="table table-bordered table-striped" id="reportTable" style="display:none;">
		<thead>
			<tr>									
				<th>Expense</th>					
				<th>Date</th>
				<th>Category</th>									
			</tr>				
		</thead>
		<tbody id="listReports">
		
		</tbody>
	</table>
	<div class="panel-heading" id="detailSection" style="display:none;">
		<div class="row">		
			<div style="padding-bottom:5px;color:green"><strong>Total Income : </strong><span id="totalIncome"></span></div>
			<div style="padding-bottom:5px;color:red"><strong>Total Expense : </strong><span id="totalExpense"></span></div>
			<div style="padding-bottom:5px;color:blue"><strong>Total Saving : </strong><span id="totalSaving"></span></div>
		</div>
	</div>
	<div class="panel-heading" id="noRecords" style="display:none;">
	</div>
</div>	

In report.js, we will get date range data to generate reports and make a ajax requests to get data and display reports.

$('#viewReport').click(function(){
	var fromDate = $('#from_date').val();
	var toDate = $('#to_date').val();		
	var action = 'getReports';
	$.ajax({
		url:'report_action.php',
		method:"POST",
		data:{fromDate:fromDate, toDate:toDate, action:action},
		dataType:"json",
		success:function(respData){				
			var reportHTML = '';
			var totalExpense = 0;
			$('#reportTable').hide();
			$('#noRecords').hide();
			respData.data.forEach(function(item){	
				reportHTML+= '<tr>';
				reportHTML+= '<td>$'+item['amount']+'</td>';
				reportHTML+= '<td>'+item['date']+'</td>';
				reportHTML+= '<td>'+item['category']+'</td>';	
				reportHTML+= '</tr>';
				totalExpense = totalExpense + parseInt(item['amount']);
				$('#reportTable').show();
			});
			$('#listReports').html(reportHTML);
			$('#detailSection').hide();
			$('#totalIncome').text("");
			$('#totalExpense').text("");
			$('#totalSaving').text("");
			respData.income.forEach(function(income){	
				$('#totalIncome').text("$"+income['total']);
				$('#totalExpense').text("$"+totalExpense);
				var finalTotal = income['total'] - totalExpense;
				$('#totalSaving').text("$"+finalTotal);
				$('#detailSection').show();
			});
			
			if(!totalExpense) {
				$('#noRecords').html("<strong>No record found!</strong>").show();
			}
		}
	});
});

we will check for action getReports in report_action and call method getReports() from class Report.php

if(!empty($_POST['action']) && $_POST['action'] == 'getReports') {
	$report->fromDate = $_POST['fromDate'];
	$report->toDate = $_POST['toDate'];
	$report->getReports();
}

and finally, we will implement the method getReports() in class Report.php to get reports.

public function getReports(){
	if($this->fromDate && $this->toDate && $_SESSION["userid"]) {				
			
		$sqlQuery = "SELECT expense.id, expense.amount, expense.date, category.name AS category
			FROM ".$this->incomeTable." AS expense 
			LEFT JOIN ".$this->incomeCategoryTable." AS category ON expense.category_id = category.id 
			WHERE expense.user_id = '".$_SESSION["userid"]."' AND expense.date BETWEEN  '".$this->fromDate."' AND '".$this->toDate."'";
				
		$stmt = $this->conn->prepare($sqlQuery);			
		$stmt->execute();
		$result = $stmt->get_result();				
		$incomeRecords = array();	
		$totalIncome = 0;
		while ($income = $result->fetch_assoc()) {			
			$totalIncome+=$income['amount'];			
		}
		if($totalIncome) {
			$row = array();
			$row['total'] = $totalIncome;
			$incomeRecords[] = $row;
		}
		
		$sqlQuery = "SELECT expense.id, expense.amount, expense.date, category.name AS category
			FROM ".$this->expenseTable." AS expense 
			LEFT JOIN ".$this->categoryTable." AS category ON expense.category_id = category.id 
			WHERE expense.date BETWEEN  '".$this->fromDate."' AND '".$this->toDate."'";
				
		$stmt = $this->conn->prepare($sqlQuery);			
		$stmt->execute();
		$result = $stmt->get_result();				
		$records = array();		
		while ($expense = $result->fetch_assoc()) { 				
			$rows = array();	
			$rows['id'] = $expense['id'];				
			$rows['amount'] = $expense['amount'];				
			$rows['date'] = $expense['date'];	
			$rows['category'] = $expense['category'];
			$records[] = $rows;
		}		
		$output = array(			
			"data"	=> 	$records,
			"income" => $incomeRecords
		);
		echo json_encode($output);
	}
}
expense-management-system-php-mysql

Leave a Reply