Files
2026-04-18 11:44:02 -04:00

190 lines
8.7 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid mt-4">
<h2 class="text-center mt-4 mb-3">Expense / Purchase</h2>
<?= $this->include('partials/academic_filter') ?>
<div class="d-flex gap-2 mb-3 justify-content-end">
<a href="<?= base_url('expenses/create') ?>" class="btn btn-success mb-3">Add New</a>
</div>
<?php if (session()->getFlashdata('success')): ?>
<div class="alert alert-success">
<?= session()->getFlashdata('success') ?>
</div>
<?php endif; ?>
<?php $totalAmount = 0.0; ?>
<div class="table-responsive">
<table id="expensesTable" class="table table-bordered table-striped align-middle w-100 no-mgmt-sticky" style="table-layout:auto;">
<thead class="table-light">
<tr>
<th>Category</th>
<th>Amount</th>
<th>Vendor</th>
<th>Purchased By</th>
<th>Date of Purchase</th>
<th>Description</th>
<th>Status</th>
<th>Approved By</th>
<th>Receipt</th>
<th>Added On</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($expenses as $e): ?>
<?php $totalAmount += (float)($e['amount'] ?? 0); ?>
<tr>
<td>
<?= esc($e['category']) ?>
<?php if (strcasecmp($e['category'], 'Donation') === 0): ?>
<span class="badge bg-info text-dark ms-1">Non-reimbursable</span>
<?php endif; ?>
</td>
<td>$<?= number_format($e['amount'], 2) ?></td>
<td>
<?php
$retail = $e['retailor'] ?? ($e['retailer'] ?? null);
echo esc($retail ?: 'N/A');
?>
</td>
<td>
<?= $e['purchaser_firstname'] || $e['purchaser_lastname']
? esc($e['purchaser_firstname'] . ' ' . $e['purchaser_lastname'])
: 'N/A' ?>
</td>
<td>
<?php
$purchaseDate = $e['date_of_purchase']
?? ($e['purchase_date']
?? ($e['purchased_at']
?? ($e['date_purchased']
?? null)));
if (!empty($purchaseDate)) {
$purchaseDisp = local_date($purchaseDate, 'm-d-Y');
} elseif (!empty($e['created_at'])) {
$purchaseDisp = local_date($e['created_at'], 'm-d-Y');
} else {
$purchaseDisp = '-';
}
echo esc($purchaseDisp);
?>
</td>
<td><?= esc($e['description']) ?></td>
<td>
<span class="badge bg-<?= $e['status'] === 'approved' ? 'success' : ($e['status'] === 'denied' ? 'danger' : 'secondary') ?>">
<?= ucfirst($e['status']) ?>
</span>
<?php if (strcasecmp($e['category'], 'Donation') === 0): ?>
<span class="badge bg-info text-dark ms-1">Donation</span>
<?php endif; ?>
</td>
<td><?= esc($e['approver_firstname'] . ' ' . $e['approver_lastname'] ?? '-') ?></td>
<!-- <td><?= esc($e['status_reason'] ?? '-') ?></td> -->
<td>
<?php if ($e['receipt_path']): ?>
<!-- <a href="<?= base_url('writable/' . $e['receipt_path']) ?>" target="_blank">View</a> -->
<a href="<?= site_url('receipts/' . $e['receipt_path']) ?>" target="_blank">View receipt</a>
<?php else: ?>
N/A
<?php endif; ?>
</td>
<td><?= esc(!empty($e['created_at']) ? local_datetime($e['created_at'], 'm-d-Y H:i') : '') ?></td>
<td class="d-flex gap-2">
<a class="btn btn-outline-secondary btn-sm" href="<?= site_url('expenses/edit/' . $e['id']) ?>">Edit</a>
<?php if (strcasecmp($e['category'], 'Donation') === 0): ?>
<span class="text-muted small align-self-center">Donation no reimbursement actions</span>
<?php elseif ($e['status'] === 'pending'): ?>
<button class="btn btn-success btn-sm" onclick="handleExpenseAction(<?= $e['id'] ?>, 'approved')">Approve</button>
<button class="btn btn-danger btn-sm" onclick="handleExpenseAction(<?= $e['id'] ?>, 'denied')">Deny</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot class="table-light">
<tr>
<th>Total</th>
<th>$<?= number_format($totalAmount, 2) ?></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</tfoot>
</table>
</div>
<!-- Removed local sticky CSS; DataTables FixedHeader handles header pinning -->
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
(function(){
const CSRF_COOKIE_NAME = <?= json_encode(config('Security')->csrfCookieName ?? (config('Security')->cookieName ?? 'csrf_cookie_name')) ?>;
function getCookie(name){
return document.cookie.split(';').map(v=>v.trim()).find(v=>v.startsWith(name+'='))?.split('=')[1] || '';
}
window.handleExpenseAction = function(id, status) {
if (!confirm(`Are you sure you want to ${status} this expense?`)) return;
const csrf = decodeURIComponent(getCookie(CSRF_COOKIE_NAME) || '');
fetch("<?= base_url('expenses/updateStatus') ?>", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrf || '<?= csrf_hash() ?>'
},
// No 'reason' sent
body: JSON.stringify({
id,
status
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
location.reload();
} else {
alert(response.error || 'Action failed.');
}
})
.catch(() => alert('Server error.'));
}
})();
</script>
<script>
// Attach DataTables FixedHeader for sticky header reliability
(function(){
function getFixedHeaderOffset(){
var total = 0;
try {
var sels = ['header.navbar.sticky-top','header.navbar.fixed-top','#navbarManagement.navbar.sticky-top','#navbarManagement.navbar.fixed-top'];
var seen = [];
sels.forEach(function(s){ document.querySelectorAll(s).forEach(function(el){ if (seen.indexOf(el) === -1) seen.push(el); }); });
document.querySelectorAll('.navbar.sticky-top, .navbar.fixed-top').forEach(function(el){ if (seen.indexOf(el) === -1) seen.push(el); });
seen.forEach(function(el){ var h = el.offsetHeight || (el.getBoundingClientRect && el.getBoundingClientRect().height) || 0; total += Math.max(0, Math.round(h)); });
} catch(_) {}
return total;
}
document.addEventListener('DOMContentLoaded', function(){
if (window.jQuery && jQuery.fn && jQuery.fn.DataTable) {
try {
jQuery('#expensesTable').DataTable({ paging:false, searching:false, info:false, order: [], fixedHeader: { header: true, headerOffset: getFixedHeaderOffset() } });
} catch(_) {}
}
// No polyfill needed when DataTables FixedHeader is active
});
})();
</script>
<?= $this->endSection() ?>