288 lines
10 KiB
PHP
288 lines
10 KiB
PHP
<?php
|
|
session_start();
|
|
require '../config.php';
|
|
require '../include_header.php';
|
|
?>
|
|
|
|
<body>
|
|
<?php require '../include_topbar.php'; ?>
|
|
<?php require '../include_master_sidebar.php'; ?>
|
|
|
|
<main id="content" class="content py-15">
|
|
<div class="container-fluid">
|
|
|
|
<!-- Page header -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-5 gap-3">
|
|
<div>
|
|
<h1 class="fs-3 mb-1">Chart of Accounts</h1>
|
|
<p class="mb-0">Manage your account code structure</p>
|
|
</div>
|
|
<div>
|
|
<a href="<?php echo $server_url?>accounting/manage_account.php" class="btn btn-primary">
|
|
<i class="ti ti-plus me-1"></i>Add Account
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats -->
|
|
<div class="row g-5 mb-5">
|
|
<div class="col-xl-3 col-md-6 col-12">
|
|
<div class="card">
|
|
<div class="card-body p-6">
|
|
<div class="d-flex align-items-start gap-3">
|
|
<div class="icon-shape icon-lg bg-primary bg-opacity-10 text-primary rounded-2">
|
|
<i class="ti ti-list-tree fs-4"></i>
|
|
</div>
|
|
<div>
|
|
<span>Total Accounts</span>
|
|
<h2 class="fw-bold mb-0" id="stat_total">—</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-md-6 col-12">
|
|
<div class="card">
|
|
<div class="card-body p-6">
|
|
<div class="d-flex align-items-start gap-3">
|
|
<div class="icon-shape icon-lg bg-success bg-opacity-10 text-success rounded-2">
|
|
<i class="ti ti-check fs-4"></i>
|
|
</div>
|
|
<div>
|
|
<span>Active</span>
|
|
<h2 class="fw-bold mb-0" id="stat_active">—</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-md-6 col-12">
|
|
<div class="card">
|
|
<div class="card-body p-6">
|
|
<div class="d-flex align-items-start gap-3">
|
|
<div class="icon-shape icon-lg bg-info bg-opacity-10 text-info rounded-2">
|
|
<i class="ti ti-trending-up fs-4"></i>
|
|
</div>
|
|
<div>
|
|
<span>Revenue Accounts</span>
|
|
<h2 class="fw-bold mb-0" id="stat_revenue">—</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-md-6 col-12">
|
|
<div class="card">
|
|
<div class="card-body p-6">
|
|
<div class="d-flex align-items-start gap-3">
|
|
<div class="icon-shape icon-lg bg-warning bg-opacity-10 text-warning rounded-2">
|
|
<i class="ti ti-trending-down fs-4"></i>
|
|
</div>
|
|
<div>
|
|
<span>Expense Accounts</span>
|
|
<h2 class="fw-bold mb-0" id="stat_expense">—</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Table -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card overflow-hidden">
|
|
<div class="card-body p-6">
|
|
|
|
<div class="mb-5 d-flex align-items-center justify-content-between gap-3 flex-wrap">
|
|
<div class="d-flex gap-2 flex-wrap">
|
|
<button class="btn btn-sm btn-outline-secondary active" onclick="filter_type('')" id="filter_all">All</button>
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="filter_type('asset')" id="filter_asset">Asset</button>
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="filter_type('liability')" id="filter_liability">Liability</button>
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="filter_type('equity')" id="filter_equity">Equity</button>
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="filter_type('revenue')" id="filter_revenue">Revenue</button>
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="filter_type('expense')" id="filter_expense">Expense</button>
|
|
</div>
|
|
<div class="position-relative" style="min-width:240px;">
|
|
<input type="search" id="search_account" class="form-control ps-9" placeholder="Search code or name…">
|
|
<span class="position-absolute top-25 ms-4">
|
|
<i class="ti ti-search text-muted"></i>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table mb-0 text-nowrap table-hover table-centered" id="account_table">
|
|
<thead class="table-primary border-light">
|
|
<tr>
|
|
<th>Code</th>
|
|
<th>Name</th>
|
|
<th>Type</th>
|
|
<th>Category</th>
|
|
<th>Level</th>
|
|
<th>Status</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
<tfoot></tfoot>
|
|
</table>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</main>
|
|
|
|
<?php require '../include_ending.php'; ?>
|
|
|
|
<script>
|
|
var search_term = '';
|
|
var active_type = '';
|
|
|
|
var type_badge = {
|
|
asset: '<span class="badge bg-primary bg-opacity-10 text-primary">Asset</span>',
|
|
liability: '<span class="badge bg-danger bg-opacity-10 text-danger">Liability</span>',
|
|
equity: '<span class="badge bg-info bg-opacity-10 text-info">Equity</span>',
|
|
revenue: '<span class="badge bg-success bg-opacity-10 text-success">Revenue</span>',
|
|
expense: '<span class="badge bg-warning bg-opacity-10 text-warning">Expense</span>',
|
|
};
|
|
|
|
var category_labels = {
|
|
ar: 'Account Receivable',
|
|
ap: 'Account Payable',
|
|
sales_tax: 'Sales Tax',
|
|
purchase_tax: 'Purchase Tax',
|
|
};
|
|
|
|
function category_badge(cat) {
|
|
if (!cat) return '<span class="text-muted small">—</span>';
|
|
var label = category_labels[cat] || cat;
|
|
return '<span class="badge bg-secondary bg-opacity-10 text-secondary">' + escape_html(label) + '</span>';
|
|
}
|
|
|
|
var level_badge = {
|
|
item: '<span class="badge bg-primary">Item</span>',
|
|
parent: '<span class="badge bg-secondary">Parent</span>',
|
|
};
|
|
|
|
function retrieve_accounts() {
|
|
return ajax_request({
|
|
url: '<?php echo $server_url?>accounting/api/engine/account.php',
|
|
autoPrepare: true,
|
|
checkRequired: 0,
|
|
action: 'read',
|
|
onSuccess: function(res) {
|
|
alasql('CREATE TABLE IF NOT EXISTS md_account');
|
|
alasql.tables.md_account.data = res.output || [];
|
|
change_page(1);
|
|
}
|
|
});
|
|
}
|
|
|
|
function change_page(page_num) {
|
|
var offset = (page_num - 1) * prop_limit;
|
|
var kw = search_term.toLowerCase();
|
|
|
|
var conditions = [];
|
|
if (kw) conditions.push(`(LOWER(account_code) LIKE '%${kw}%' OR LOWER(account_name) LIKE '%${kw}%')`);
|
|
if (active_type) conditions.push(`account_type = '${active_type}'`);
|
|
|
|
var where = conditions.length ? 'WHERE ' + conditions.join(' AND ') : '';
|
|
var filtered = alasql(`SELECT * FROM md_account ${where} ORDER BY account_code`);
|
|
|
|
var page = generate_pagination('account_table', filtered.length);
|
|
$('table#account_table tfoot').html(page);
|
|
|
|
if (filtered.length === 0) {
|
|
$('table#account_table tbody').html(
|
|
'<tr><td colspan="8" class="text-center py-5 text-muted">No accounts found.</td></tr>'
|
|
);
|
|
return;
|
|
}
|
|
|
|
var page_data = filtered.slice(offset, offset + prop_limit);
|
|
var body = '';
|
|
$.each(page_data, function(i, item) {
|
|
var is_item = item.is_posting == 1;
|
|
var indent = is_item && item.parent_code ? 'ps-4' : '';
|
|
var prefix = is_item && item.parent_code ? '<i class="ti ti-corner-down-right text-muted me-1 small"></i>' : '';
|
|
body += `<tr class="${is_item ? '' : 'table-light'}">
|
|
<td class="py-3 fw-semibold ${indent}">${prefix}${escape_html(item.account_code)}</td>
|
|
<td class="py-3">${escape_html(item.account_name)}</td>
|
|
<td class="py-3">${type_badge[item.account_type] || item.account_type}</td>
|
|
<td class="py-3">${category_badge(item.account_category)}</td>
|
|
<td class="py-3">${is_item ? level_badge.item : level_badge.parent}</td>
|
|
<td class="py-3">${item.status ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-secondary">Inactive</span>'}</td>
|
|
<td class="py-3">
|
|
<a href="<?php echo $server_url?>accounting/manage_account.php?id=${item.id}"><i class="ti ti-eye fs-5"></i></a>
|
|
<a href="javascript:;" class="link-danger ms-2" onclick="remove_account(${item.id}, $(this))"><i class="ti ti-trash fs-5"></i></a>
|
|
</td>
|
|
</tr>`;
|
|
});
|
|
$('table#account_table tbody').html(body);
|
|
}
|
|
|
|
function filter_type(type) {
|
|
active_type = type;
|
|
$('.btn[id^="filter_"]').removeClass('active');
|
|
$('#filter_' + (type || 'all')).addClass('active');
|
|
change_page(1);
|
|
}
|
|
|
|
function remove_account(id, element) {
|
|
return ajax_request({
|
|
url: '<?php echo $server_url?>accounting/api/engine/remove_account.php',
|
|
autoPrepare: true,
|
|
checkRequired: 0,
|
|
action: 'delete',
|
|
data: { id: id },
|
|
onSuccess: function() {
|
|
element.closest('tr').remove();
|
|
}
|
|
});
|
|
}
|
|
|
|
function retrieve_stats() {
|
|
return ajax_request({
|
|
url: '<?php echo $server_url?>accounting/api/engine/account_stats.php',
|
|
autoPrepare: true,
|
|
checkRequired: 0,
|
|
action: 'read',
|
|
onSuccess: function(res) {
|
|
$('#stat_total').text(res.total || 0);
|
|
$('#stat_active').text(res.active || 0);
|
|
$('#stat_revenue').text(res.revenue || 0);
|
|
$('#stat_expense').text(res.expense || 0);
|
|
}
|
|
});
|
|
}
|
|
|
|
function debounce(fn, ms) {
|
|
var t;
|
|
return function() { var a = arguments, c = this; clearTimeout(t); t = setTimeout(function() { fn.apply(c, a); }, ms); };
|
|
}
|
|
|
|
$('#search_account').on('input', debounce(function() {
|
|
search_term = $(this).val().trim();
|
|
change_page(1);
|
|
}, 200));
|
|
|
|
$(async function() {
|
|
try {
|
|
await retrieve_stats();
|
|
await retrieve_accounts();
|
|
} catch(e) { console.log(e); }
|
|
});
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|