168 lines
7.0 KiB
PHP
168 lines
7.0 KiB
PHP
<?php
|
|
$page_title = "Invoices";
|
|
require_once 'includes/header.php';
|
|
|
|
$search = $_GET['search'] ?? '';
|
|
$where = "WHERE i.deleted_at IS NULL";
|
|
$params = [];
|
|
|
|
if ($search) {
|
|
$where .= " AND (i.invoice_number LIKE ? OR c.name LIKE ?)";
|
|
$params[] = "%$search%";
|
|
$params[] = "%$search%";
|
|
}
|
|
|
|
$query = "SELECT i.*, c.name as customer_name, c.email as customer_email
|
|
FROM invoices i
|
|
JOIN customers c ON i.customer_id = c.id
|
|
$where
|
|
ORDER BY i.created_at DESC";
|
|
$stmt = db()->prepare($query);
|
|
$stmt->execute($params);
|
|
$invoices = $stmt->fetchAll();
|
|
|
|
$msg = $_GET['msg'] ?? '';
|
|
$error = $_GET['error'] ?? '';
|
|
?>
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h2 class="h3 mb-0">Invoices</h2>
|
|
</div>
|
|
|
|
<?php if ($msg): ?>
|
|
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
|
<?= e($msg) ?>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($error): ?>
|
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
|
<?= e($error) ?>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="card border-0 shadow-sm mb-4">
|
|
<div class="card-body">
|
|
<form method="GET" class="row g-3">
|
|
<div class="col-md-10">
|
|
<div class="input-group">
|
|
<span class="input-group-text bg-transparent border-end-0">
|
|
<i class="bi bi-search text-muted"></i>
|
|
</span>
|
|
<input type="text" name="search" class="form-control border-start-0 ps-0"
|
|
placeholder="Search by number or customer..." value="<?= e($search) ?>">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button type="submit" class="btn btn-light w-100">Search</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead class="bg-light">
|
|
<tr>
|
|
<th class="ps-4">Invoice #</th>
|
|
<th>Customer</th>
|
|
<th>Date</th>
|
|
<th>Due Date</th>
|
|
<th>Status</th>
|
|
<th class="text-end">Total</th>
|
|
<th class="text-end pe-4">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if (empty($invoices)): ?>
|
|
<tr>
|
|
<td colspan="7" class="text-center py-5 text-muted">
|
|
No invoices found.
|
|
</td>
|
|
</tr>
|
|
<?php else: ?>
|
|
<?php foreach ($invoices as $i): ?>
|
|
<tr>
|
|
<td class="ps-4 fw-medium"><?= e($i['invoice_number']) ?></td>
|
|
<td>
|
|
<div><?= e($i['customer_name']) ?></div>
|
|
<small class="text-muted"><?= e($i['customer_email']) ?></small>
|
|
</td>
|
|
<td><?= date('M d, Y', strtotime($i['issue_date'])) ?></td>
|
|
<td><?= date('M d, Y', strtotime($i['due_date'])) ?></td>
|
|
<td>
|
|
<?php
|
|
$badge_class = 'bg-secondary';
|
|
switch($i['status']) {
|
|
case 'Paid': $badge_class = 'bg-success'; break;
|
|
case 'Unpaid': $badge_class = 'bg-warning text-dark'; break;
|
|
case 'Partial': $badge_class = 'bg-info text-dark'; break;
|
|
case 'Overdue': $badge_class = 'bg-danger'; break;
|
|
}
|
|
?>
|
|
<span class="badge rounded-pill <?= $badge_class ?>">
|
|
<?= $i['status'] ?>
|
|
</span>
|
|
</td>
|
|
<td class="text-end fw-bold"><?= format_currency($i['total_amount']) ?></td>
|
|
<td class="text-end pe-4">
|
|
<div class="btn-group">
|
|
<a href="invoice_pdf.php?id=<?= $i['id'] ?>" class="btn btn-sm btn-outline-primary" title="Download PDF" target="_blank">
|
|
<i class="bi bi-file-pdf"></i>
|
|
</a>
|
|
<a href="send_document.php?type=invoice&id=<?= $i['id'] ?>" class="btn btn-sm btn-outline-info" title="Email to Customer" onclick="return confirm('Email this invoice to <?= e($i['customer_email']) ?>?')">
|
|
<i class="bi bi-envelope"></i>
|
|
</a>
|
|
<?php if ($i['status'] !== 'Paid'): ?>
|
|
<button type="button" class="btn btn-sm btn-outline-success" title="Mark as Paid" onclick="markAsPaid(<?= $i['id'] ?>)">
|
|
<i class="bi bi-check-circle"></i>
|
|
</button>
|
|
<a href="send_document.php?type=invoice&id=<?= $i['id'] ?>&reminder=1" class="btn btn-sm btn-outline-warning" title="Send Reminder" onclick="return confirm('Send payment reminder to <?= e($i['customer_email']) ?>?')">
|
|
<i class="bi bi-bell"></i>
|
|
</a>
|
|
<?php endif; ?>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function markAsPaid(id) {
|
|
if (confirm('Mark this invoice as paid?')) {
|
|
const form = document.createElement('form');
|
|
form.method = 'POST';
|
|
form.action = 'invoice_update_status.php';
|
|
|
|
const idInput = document.createElement('input');
|
|
idInput.type = 'hidden';
|
|
idInput.name = 'id';
|
|
idInput.value = id;
|
|
form.appendChild(idInput);
|
|
|
|
const statusInput = document.createElement('input');
|
|
statusInput.type = 'hidden';
|
|
statusInput.name = 'status';
|
|
statusInput.value = 'Paid';
|
|
form.appendChild(statusInput);
|
|
|
|
const csrfInput = document.createElement('input');
|
|
csrfInput.type = 'hidden';
|
|
csrfInput.name = 'csrf_token';
|
|
csrfInput.value = '<?= csrf_token() ?>';
|
|
form.appendChild(csrfInput);
|
|
|
|
document.body.appendChild(form);
|
|
form.submit();
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<?php require_once 'includes/footer.php'; ?>
|