296 lines
14 KiB
PHP
296 lines
14 KiB
PHP
<?php
|
|
require_once __DIR__ . "/../includes/functions.php";
|
|
require_permission("expenses_view");
|
|
require_once __DIR__ . '/../db/config.php';
|
|
$pdo = db();
|
|
|
|
$message = '';
|
|
|
|
// Handle Create/Update
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
|
if ($_POST['action'] === 'save_expense') {
|
|
if (!has_permission('expenses_add') && !has_permission('expenses_edit')) {
|
|
$message = '<div class="alert alert-danger">Access Denied: You do not have permission.</div>';
|
|
} else {
|
|
$id = isset($_POST['id']) ? (int)$_POST['id'] : null;
|
|
$category_id = $_POST['category_id'];
|
|
$outlet_id = $_POST['outlet_id'];
|
|
$amount = $_POST['amount'];
|
|
$description = trim($_POST['description']);
|
|
$expense_date = $_POST['expense_date'];
|
|
|
|
if (empty($category_id) || empty($amount) || empty($expense_date)) {
|
|
$message = '<div class="alert alert-danger">Category, amount, and date are required.</div>';
|
|
} else {
|
|
try {
|
|
if ($id) {
|
|
$stmt = $pdo->prepare("UPDATE expenses SET category_id = ?, outlet_id = ?, amount = ?, description = ?, expense_date = ? WHERE id = ?");
|
|
$stmt->execute([$category_id, $outlet_id, $amount, $description, $expense_date, $id]);
|
|
$message = '<div class="alert alert-success">Expense updated successfully!</div>';
|
|
} else {
|
|
$stmt = $pdo->prepare("INSERT INTO expenses (category_id, outlet_id, amount, description, expense_date) VALUES (?, ?, ?, ?, ?)");
|
|
$stmt->execute([$category_id, $outlet_id, $amount, $description, $expense_date]);
|
|
$message = '<div class="alert alert-success">Expense recorded successfully!</div>';
|
|
}
|
|
} catch (PDOException $e) {
|
|
$message = '<div class="alert alert-danger">Database error: ' . $e->getMessage() . '</div>';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle Delete
|
|
if (isset($_GET['delete'])) {
|
|
if (!has_permission('expenses_del')) {
|
|
$message = '<div class="alert alert-danger">Access Denied: You do not have permission to delete expenses.</div>';
|
|
} else {
|
|
try {
|
|
$id = $_GET['delete'];
|
|
$pdo->prepare("DELETE FROM expenses WHERE id = ?")->execute([$id]);
|
|
header("Location: expenses.php?deleted=1");
|
|
exit;
|
|
} catch (PDOException $e) {
|
|
$message = '<div class="alert alert-danger">Error deleting expense: ' . $e->getMessage() . '</div>';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isset($_GET['deleted'])) {
|
|
$message = '<div class="alert alert-success">Expense deleted successfully!</div>';
|
|
}
|
|
|
|
$expense_categories = $pdo->query("SELECT * FROM expense_categories ORDER BY name")->fetchAll();
|
|
$outlets = $pdo->query("SELECT * FROM outlets ORDER BY name")->fetchAll();
|
|
|
|
$search = $_GET['search'] ?? '';
|
|
$category_filter = $_GET['category_filter'] ?? '';
|
|
$outlet_filter = $_GET['outlet_filter'] ?? '';
|
|
|
|
$params = [];
|
|
$where = [];
|
|
|
|
$query = "SELECT e.*, ec.name as category_name, o.name as outlet_name
|
|
FROM expenses e
|
|
LEFT JOIN expense_categories ec ON e.category_id = ec.id
|
|
LEFT JOIN outlets o ON e.outlet_id = o.id";
|
|
|
|
if ($search) {
|
|
$where[] = "e.description LIKE ?";
|
|
$params[] = "%$search%";
|
|
}
|
|
|
|
if ($category_filter) {
|
|
$where[] = "e.category_id = ?";
|
|
$params[] = $category_filter;
|
|
}
|
|
|
|
if ($outlet_filter) {
|
|
$where[] = "e.outlet_id = ?";
|
|
$params[] = $outlet_filter;
|
|
}
|
|
|
|
if (!empty($where)) {
|
|
$query .= " WHERE " . implode(" AND ", $where);
|
|
}
|
|
|
|
$query .= " ORDER BY e.expense_date DESC, e.id DESC";
|
|
|
|
$expenses_pagination = paginate_query($pdo, $query, $params);
|
|
$expenses = $expenses_pagination['data'];
|
|
|
|
include 'includes/header.php';
|
|
?>
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h2 class="fw-bold mb-1">Expenses</h2>
|
|
<p class="text-muted mb-0">Track and manage business expenditures</p>
|
|
</div>
|
|
<?php if (has_permission('expenses_add')): ?>
|
|
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#expenseModal" onclick="resetExpenseModal()">
|
|
<i class="bi bi-plus-lg"></i> Add Expense
|
|
</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?= $message ?>
|
|
|
|
<div class="card border-0 shadow-sm mb-4">
|
|
<div class="card-body">
|
|
<form method="GET" class="row g-3">
|
|
<div class="col-md-4">
|
|
<input type="text" name="search" class="form-control" placeholder="Search description..." value="<?= htmlspecialchars($search) ?>">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<select name="category_filter" class="form-select">
|
|
<option value="">All Categories</option>
|
|
<?php foreach ($expense_categories as $cat): ?>
|
|
<option value="<?= $cat['id'] ?>" <?= $category_filter == $cat['id'] ? 'selected' : '' ?>><?= htmlspecialchars($cat['name']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<select name="outlet_filter" class="form-select">
|
|
<option value="">All Outlets</option>
|
|
<?php foreach ($outlets as $outlet): ?>
|
|
<option value="<?= $outlet['id'] ?>" <?= $outlet_filter == $outlet['id'] ? 'selected' : '' ?>><?= htmlspecialchars($outlet['name']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2 d-flex gap-2">
|
|
<button type="submit" class="btn btn-outline-primary w-100">Filter</button>
|
|
<?php if ($search || $category_filter || $outlet_filter): ?>
|
|
<a href="expenses.php" class="btn btn-outline-secondary"><i class="bi bi-x"></i></a>
|
|
<?php endif; ?>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-body p-0">
|
|
<div class="p-3 border-bottom bg-light">
|
|
<?php render_pagination_controls($expenses_pagination); ?>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead class="bg-light">
|
|
<tr>
|
|
<th class="ps-4">Date</th>
|
|
<th>Category</th>
|
|
<th>Outlet</th>
|
|
<th>Description</th>
|
|
<th>Amount</th>
|
|
<th class="text-end pe-4">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($expenses as $exp): ?>
|
|
<tr>
|
|
<td class="ps-4 fw-medium"><?= date('M d, Y', strtotime($exp['expense_date'])) ?></td>
|
|
<td><span class="badge bg-info bg-opacity-10 text-info"><?= htmlspecialchars($exp['category_name']) ?></span></td>
|
|
<td><?= htmlspecialchars($exp['outlet_name']) ?></td>
|
|
<td><?= htmlspecialchars($exp['description']) ?></td>
|
|
<td class="fw-bold"><?= format_currency($exp['amount']) ?></td>
|
|
<td class="text-end pe-4">
|
|
<?php if (has_permission('expenses_edit')): ?>
|
|
<button type="button" class="btn btn-sm btn-outline-primary me-1"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#expenseModal"
|
|
onclick='editExpense(<?= htmlspecialchars(json_encode($exp), ENT_QUOTES, 'UTF-8') ?>)'>
|
|
<i class="bi bi-pencil"></i>
|
|
</button>
|
|
<?php endif; ?>
|
|
<?php if (has_permission('expenses_del')): ?>
|
|
<a href="?delete=<?= $exp['id'] ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure?')"><i class="bi bi-trash"></i></a>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php if (empty($expenses)): ?>
|
|
<tr>
|
|
<td colspan="6" class="text-center py-4 text-muted">No expenses found.</td>
|
|
</tr>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="p-3 border-top bg-light">
|
|
<?php render_pagination_controls($expenses_pagination); ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Expense Modal -->
|
|
<div class="modal fade" id="expenseModal" tabindex="-1" aria-labelledby="expenseModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<form method="POST">
|
|
<input type="hidden" name="action" value="save_expense">
|
|
<input type="hidden" name="id" id="expense_id">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="expenseModalLabel">Add New Expense</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Category <span class="text-danger">*</span></label>
|
|
<select name="category_id" id="expense_category_id" class="form-select" required>
|
|
<option value="">Select Category</option>
|
|
<?php foreach ($expense_categories as $cat): ?>
|
|
<option value="<?= $cat['id'] ?>"><?= htmlspecialchars($cat['name']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Outlet <span class="text-danger">*</span></label>
|
|
<select name="outlet_id" id="expense_outlet_id" class="form-select" required>
|
|
<?php foreach ($outlets as $outlet): ?>
|
|
<option value="<?= $outlet['id'] ?>"><?= htmlspecialchars($outlet['name']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Amount <span class="text-danger">*</span></label>
|
|
<div class="input-group">
|
|
<span class="input-group-text">$</span>
|
|
<input type="number" step="0.01" name="amount" id="expense_amount" class="form-control" required>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Date <span class="text-danger">*</span></label>
|
|
<input type="date" name="expense_date" id="expense_date" class="form-control" value="<?= date('Y-m-d') ?>" required>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Description</label>
|
|
<textarea name="description" id="expense_description" class="form-control" rows="3" placeholder="What was this expense for?"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary" id="expenseSubmitBtn">Record Expense</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function resetExpenseModal() {
|
|
document.getElementById('expenseModalLabel').innerText = 'Add New Expense';
|
|
document.getElementById('expenseSubmitBtn').innerText = 'Record Expense';
|
|
document.getElementById('expense_id').value = '';
|
|
document.getElementById('expense_category_id').value = '';
|
|
document.getElementById('expense_outlet_id').value = '<?= !empty($outlets) ? $outlets[0]['id'] : '' ?>';
|
|
document.getElementById('expense_amount').value = '';
|
|
document.getElementById('expense_date').value = '<?= date('Y-m-d') ?>';
|
|
document.getElementById('expense_description').value = '';
|
|
}
|
|
|
|
function editExpense(exp) {
|
|
document.getElementById('expenseModalLabel').innerText = 'Edit Expense';
|
|
document.getElementById('expenseSubmitBtn').innerText = 'Save Changes';
|
|
document.getElementById('expense_id').value = exp.id;
|
|
document.getElementById('expense_category_id').value = exp.category_id;
|
|
document.getElementById('expense_outlet_id').value = exp.outlet_id;
|
|
document.getElementById('expense_amount').value = exp.amount;
|
|
document.getElementById('expense_date').value = exp.expense_date;
|
|
document.getElementById('expense_description').value = exp.description;
|
|
}
|
|
</script>
|
|
|
|
<?php include 'includes/footer.php'; ?>
|