Send journal lines, gl_id and list filters inside the ajax data payload where ajax_request reads them, select period when replacing a manual journal, and show ledger amounts to two decimals.
231 lines
9.5 KiB
PHP
231 lines
9.5 KiB
PHP
<?php
|
|
session_start();
|
|
require '../config.php';
|
|
require '../include_header.php';
|
|
?>
|
|
|
|
<body>
|
|
<?php require '../include_topbar.php'; ?>
|
|
<?php require '../include_sidebar_ac.php'; ?>
|
|
|
|
<main id="content" class="content py-15">
|
|
<div class="container-fluid">
|
|
|
|
<div class="row mb-6">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h1 class="fs-3 mb-1">Journal Entries</h1>
|
|
<p class="mb-0 text-muted">All posted general ledger entries</p>
|
|
</div>
|
|
<a href="<?php echo $server_url?>journal/new.php" class="btn btn-primary">
|
|
<i class="ti ti-plus me-1"></i>New Manual Entry
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filters -->
|
|
<div class="card p-4 mb-4">
|
|
<div class="row g-2 align-items-end">
|
|
<div class="col-auto">
|
|
<label class="form-label mb-1 small">Period</label>
|
|
<input type="month" id="f_period" class="form-control form-control-sm" style="width:160px;">
|
|
</div>
|
|
<div class="col-auto">
|
|
<label class="form-label mb-1 small">Source Type</label>
|
|
<select id="f_source" class="form-select form-select-sm" style="width:200px;">
|
|
<option value="">All Types</option>
|
|
<option value="invoice">Invoice</option>
|
|
<option value="credit_note">Credit Note</option>
|
|
<option value="purchase_invoice">Purchase Invoice</option>
|
|
<option value="supplier_credit_note">Supplier Credit Note</option>
|
|
<option value="receipt">Receipt</option>
|
|
<option value="payment">Payment</option>
|
|
<option value="manual">Manual</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-auto">
|
|
<button class="btn btn-secondary btn-sm" onclick="load_listing()">
|
|
<i class="ti ti-search me-1"></i>Load
|
|
</button>
|
|
</div>
|
|
<div class="col ms-auto d-flex align-items-end justify-content-end">
|
|
<span id="jl_badge" class="text-muted small"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Table -->
|
|
<div class="card">
|
|
<div class="card-body p-0">
|
|
<table class="table table-hover mb-0 table-centered">
|
|
<thead class="table-primary border-light">
|
|
<tr>
|
|
<th>Document</th>
|
|
<th>Type</th>
|
|
<th>Contact / Note</th>
|
|
<th>Period</th>
|
|
<th class="text-end">Debit</th>
|
|
<th class="text-end">Credit</th>
|
|
<th>Posted At</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="jl_tbody">
|
|
<tr><td colspan="8" class="text-center text-muted py-5">Use filters and click Load.</td></tr>
|
|
</tbody>
|
|
<tfoot></tfoot>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</main>
|
|
|
|
<!-- GL Detail Modal -->
|
|
<div class="modal fade" id="gl_detail_modal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Journal Entry Detail</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body" id="gl_detail_body">
|
|
<div class="text-center text-muted py-5">Loading...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php require '../include_ending.php'; ?>
|
|
|
|
<script>
|
|
var src_labels = {
|
|
invoice: 'Invoice', credit_note: 'Credit Note',
|
|
purchase_invoice: 'Purchase Invoice', supplier_credit_note: 'Supplier Credit Note',
|
|
receipt: 'Receipt', payment: 'Payment', manual: 'Manual',
|
|
};
|
|
var type_badge_cls = {
|
|
invoice: 'bg-primary-subtle text-primary',
|
|
receipt: 'bg-success-subtle text-success',
|
|
payment: 'bg-warning-subtle text-warning',
|
|
purchase_invoice: 'bg-danger-subtle text-danger',
|
|
supplier_credit_note: 'bg-secondary-subtle text-secondary',
|
|
credit_note: 'bg-info-subtle text-info',
|
|
manual: 'bg-dark-subtle text-dark',
|
|
};
|
|
|
|
function load_listing() {
|
|
var period = document.getElementById('f_period').value; // "YYYY-MM"
|
|
var source = document.getElementById('f_source').value;
|
|
|
|
// The engine filters on a date range, not a period, so turn the chosen
|
|
// month into its first and last day.
|
|
var date_from = '', date_to = '';
|
|
if (/^\d{4}-\d{2}$/.test(period)) {
|
|
var ym = period.split('-');
|
|
var last_day = new Date(parseInt(ym[0]), parseInt(ym[1]), 0).getDate();
|
|
date_from = period + '-01';
|
|
date_to = period + '-' + ('0' + last_day).slice(-2);
|
|
}
|
|
|
|
// Payload fields must go inside `data` — ajax_request() ignores unknown
|
|
// top-level options, so as siblings of `url` these were never sent and the
|
|
// month / source filters silently did nothing.
|
|
ajax_request({
|
|
url: server_url + 'accounting/api/engine/get_journal_listing.php',
|
|
autoPrepare: true,
|
|
checkRequired: 0,
|
|
action: 'read',
|
|
data: {
|
|
source_type: source,
|
|
date_from: date_from,
|
|
date_to: date_to
|
|
},
|
|
onSuccess: function(res) {
|
|
var rows = res.output || [];
|
|
document.getElementById('jl_badge').textContent = rows.length + ' entries';
|
|
|
|
if (rows.length === 0) {
|
|
document.getElementById('jl_tbody').innerHTML =
|
|
'<tr><td colspan="8" class="text-center text-muted py-5">No entries found.</td></tr>';
|
|
return;
|
|
}
|
|
|
|
var html = '';
|
|
rows.forEach(function(r) {
|
|
var cls = type_badge_cls[r.source_type] || 'bg-secondary-subtle text-secondary';
|
|
var label = src_labels[r.source_type] || r.source_type;
|
|
html += '<tr>' +
|
|
'<td class="fw-semibold">' + escape_html(r.doc_number || ('GL-' + r.id)) + '</td>' +
|
|
'<td><span class="badge rounded-pill ' + cls + ' small">' + escape_html(label) + '</span></td>' +
|
|
'<td class="small text-muted">' + escape_html(r.contact_name || '—') + '</td>' +
|
|
'<td class="small">' + escape_html(r.period) + '</td>' +
|
|
'<td class="text-end small">' + format_number(r.total_debit, 2) + '</td>' +
|
|
'<td class="text-end small">' + format_number(r.total_credit, 2) + '</td>' +
|
|
'<td class="small text-muted">' + escape_html(r.posted_at) + '</td>' +
|
|
'<td><a href="javascript:;" onclick="view_detail(' + r.id + ')"><i class="ti ti-eye fs-5"></i></a>' +
|
|
(r.source_type === 'manual' ? ' <a href="' + server_url + 'journal/new.php?gl_id=' + r.id + '" class="ms-2"><i class="ti ti-edit fs-5"></i></a>' : '') +
|
|
'</td>' +
|
|
'</tr>';
|
|
});
|
|
document.getElementById('jl_tbody').innerHTML = html;
|
|
}
|
|
});
|
|
}
|
|
|
|
function view_detail(gl_id) {
|
|
document.getElementById('gl_detail_body').innerHTML = '<div class="text-center text-muted py-5">Loading...</div>';
|
|
var modal = new bootstrap.Modal(document.getElementById('gl_detail_modal'));
|
|
modal.show();
|
|
|
|
ajax_request({
|
|
url: server_url + 'accounting/api/engine/get_journal_detail.php',
|
|
autoPrepare: true,
|
|
checkRequired: 0,
|
|
action: 'read',
|
|
// Inside `data`, or it is never sent and the engine answers
|
|
// "gl_id is required." for every row.
|
|
data: { gl_id: gl_id },
|
|
onSuccess: function(res) {
|
|
var d = res.output;
|
|
var h = d.header;
|
|
var lines = d.lines || [];
|
|
|
|
var lines_html = lines.map(function(l) {
|
|
return '<tr>' +
|
|
'<td>' + escape_html(l.account_code) + '</td>' +
|
|
'<td>' + escape_html(l.account_name) + '</td>' +
|
|
'<td class="text-muted small">' + escape_html(l.description || '—') + '</td>' +
|
|
'<td class="text-end">' + (parseFloat(l.debit) ? format_number(l.debit, 2) : '—') + '</td>' +
|
|
'<td class="text-end">' + (parseFloat(l.credit) ? format_number(l.credit, 2) : '—') + '</td>' +
|
|
'</tr>';
|
|
}).join('');
|
|
|
|
document.getElementById('gl_detail_body').innerHTML =
|
|
'<div class="row g-3 mb-4">' +
|
|
'<div class="col-6"><p class="text-muted small mb-0">Date</p><strong>' + escape_html(format_date(h.journal_date)) + '</strong></div>' +
|
|
'<div class="col-6"><p class="text-muted small mb-0">Period</p><strong>' + escape_html(h.period) + '</strong></div>' +
|
|
'<div class="col-6"><p class="text-muted small mb-0">Reference</p><strong>' + escape_html(h.reference || '—') + '</strong></div>' +
|
|
'<div class="col-6"><p class="text-muted small mb-0">Source</p><strong>' + escape_html(src_labels[h.source_type] || h.source_type) + '</strong></div>' +
|
|
(h.description ? '<div class="col-12"><p class="text-muted small mb-0">Description</p><span>' + escape_html(h.description) + '</span></div>' : '') +
|
|
'</div>' +
|
|
'<table class="table table-sm">' +
|
|
'<thead class="table-light"><tr><th>Code</th><th>Account</th><th>Description</th><th class="text-end">Debit</th><th class="text-end">Credit</th></tr></thead>' +
|
|
'<tbody>' + lines_html + '</tbody>' +
|
|
'</table>';
|
|
}
|
|
});
|
|
}
|
|
|
|
// Auto-load current month on init
|
|
var now = new Date();
|
|
document.getElementById('f_period').value =
|
|
now.getFullYear() + '-' + String(now.getMonth()+1).padStart(2,'0');
|
|
load_listing();
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|