Files
wms-app/app/accounting/vat_report.php
T
Thanakorn c89b28da4c Fix QA review findings: server-side validation, notes encoding, dashboard totals
Validate document lines on the server and recompute their totals, store notes with quotes/markup/emoji (utf8mb4, idempotent escaping, decode in form fields), exclude transfers from company-wide stock in/out, count revenue from confirmed orders only, one low-stock rule everywhere, list unapproved lots, natural bin sort, stable order/PO sort, status tiles that add up.
2026-09-19 10:58:42 +07:00

261 lines
11 KiB
PHP
Raw 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.
<?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-5">
<div class="col-12">
<h1 class="fs-3 mb-1">VAT Report</h1>
<p class="mb-0 text-muted">Output VAT (Sales Tax) and Input VAT (Purchase Tax) from posted journal entries</p>
</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">From Date</label>
<input type="text" id="f_from" class="form-control form-control-sm" placeholder="DD/MM/YYYY" style="width:160px;">
</div>
<div class="col-auto">
<label class="form-label mb-1 small">To Date</label>
<input type="text" id="f_to" class="form-control form-control-sm" placeholder="DD/MM/YYYY" style="width:160px;">
</div>
<div class="col-auto">
<label class="form-label mb-1 small">Category</label>
<select id="f_cat" class="form-select form-select-sm" style="width:180px;">
<option value="">All</option>
<option value="sales_tax">Output VAT (Sales)</option>
<option value="purchase_tax">Input VAT (Purchase)</option>
</select>
</div>
<div class="col-auto">
<label class="form-label mb-1 small">Department</label>
<select id="f_department" class="form-select form-select-sm" style="width:200px;">
<option value="0">All Departments</option>
</select>
</div>
<div class="col-auto">
<button class="btn btn-secondary btn-sm" onclick="load_report()">
<i class="ti ti-search me-1"></i>Run
</button>
</div>
<div class="col-auto">
<div class="dropdown">
<a href="#" role="button" class="btn btn-outline-secondary btn-sm text-nowrap" data-bs-toggle="dropdown" aria-expanded="false">
<i class="ti ti-download me-1"></i>Export
</a>
<div class="dropdown-menu dropdown-menu-end p-0" style="min-width:160px;">
<div class="d-flex gap-3 align-items-center border-bottom px-3 py-3">
<i class="ti ti-download"></i><div><h4 class="mb-0 small">Export</h4></div>
</div>
<div class="p-3 d-flex flex-column gap-1 small lh-lg">
<a href="javascript:;" class="ac-export-link" onclick="export_report('csv')"><i class="ti ti-file-type-csv me-2"></i>CSV</a>
<a href="javascript:;" class="ac-export-link" onclick="export_report('xlsx')"><i class="ti ti-file-type-xls me-2"></i>Excel</a>
<a href="javascript:;" class="ac-export-link" onclick="export_report('pdf')"><i class="ti ti-file-type-pdf me-2"></i>PDF</a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Summary cards -->
<div class="row g-3 mb-4" id="vat_summary" style="display:none;">
<div class="col-md-4">
<div class="card border-0 shadow-sm">
<div class="card-body">
<p class="text-muted small mb-1">Total Debit</p>
<h4 class="mb-0 fw-bold" id="vat_debit">—</h4>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-0 shadow-sm">
<div class="card-body">
<p class="text-muted small mb-1">Total Credit</p>
<h4 class="mb-0 fw-bold" id="vat_credit">—</h4>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-0 shadow-sm">
<div class="card-body">
<p class="text-muted small mb-1">Net VAT (Credit − Debit)</p>
<h4 class="mb-0 fw-bold" id="vat_net">—</h4>
</div>
</div>
</div>
</div>
<!-- Table -->
<div class="card">
<div class="card-body p-0">
<table class="table table-hover mb-0 table-sm">
<thead class="table-primary border-light">
<tr>
<th>Date</th>
<th>Period</th>
<th>Type</th>
<th>Department</th>
<th>Reference</th>
<th>Account</th>
<th>Category</th>
<th>Description</th>
<th class="text-end">Debit</th>
<th class="text-end">Credit</th>
</tr>
</thead>
<tbody id="vat_tbody">
<tr><td colspan="10" class="text-center text-muted py-5">Select a date range and click Run.</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</main>
<?php require '../include_ending.php'; ?>
<script>
var now = new Date();
var curMonth = now.getFullYear() + '-' + String(now.getMonth()+1).padStart(2,'0');
document.getElementById('f_from').value = curMonth + '-01';
document.getElementById('f_to').value = curMonth + '-' + String(new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate()).padStart(2,'0');
flatpickr('#f_from, #f_to', { dateFormat: 'Y-m-d', altInput: true, altFormat: 'd/m/Y', allowInput: true });
var export_data = [];
var cat_labels = { sales_tax: 'Output VAT', purchase_tax: 'Input VAT' };
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',
};
load_departments();
function load_departments() {
ajax_request({
url: server_url + 'accounting/api/engine/department.php',
autoPrepare: true,
checkRequired: 0,
noLoading: true,
action: 'read',
onSuccess: function(res) {
var html = '<option value="0">All Departments</option>';
(res.output || []).filter(function(d){ return String(d.status) === '1'; }).forEach(function(d) {
html += '<option value="' + d.id + '">' + escape_html(d.dept_code + ' - ' + d.dept_name) + '</option>';
});
document.getElementById('f_department').innerHTML = html;
}
});
}
function load_report() {
var from = document.getElementById('f_from').value;
var to = document.getElementById('f_to').value;
var cat = document.getElementById('f_cat').value;
var dept = document.getElementById('f_department').value || '0';
if (!from || !to) { bootbox.alert('Select both dates.'); return; }
export_data = [];
document.getElementById('vat_tbody').innerHTML =
'<tr><td colspan="10" class="text-center text-muted py-5"><span class="spinner-border spinner-border-sm me-2"></span>Loading...</td></tr>';
document.getElementById('vat_summary').style.display = 'none';
ajax_request({
url: server_url + 'accounting/api/engine/get_vat_report.php',
autoPrepare: true,
checkRequired: 0,
noLoading: true,
action: 'read',
data: { from_date: from, to_date: to, categories: cat ? [cat] : [], department_id: dept },
onSuccess: function(res) {
var d = res.output;
var rows = d.rows || [];
document.getElementById('vat_debit').textContent = format_number(d.total_debit);
document.getElementById('vat_credit').textContent = format_number(d.total_credit);
document.getElementById('vat_net').textContent = format_number(parseFloat(d.total_credit) - parseFloat(d.total_debit));
document.getElementById('vat_summary').style.display = '';
if (rows.length === 0) {
document.getElementById('vat_tbody').innerHTML =
'<tr><td colspan="10" class="text-center text-muted py-5">No VAT entries found.</td></tr>';
return;
}
export_data = rows.map(function(r) {
return {
date: r.entry_date||'', period: r.period||'',
type: src_labels[r.source_type]||r.source_type,
department: r.dept_code||'',
reference: r.reference||'',
account: r.account_code + ' ' + r.account_name,
category: cat_labels[r.account_category]||r.account_category,
description: r.description||'',
debit: parseFloat(r.debit)||'',
credit: parseFloat(r.credit)||'',
};
});
var html = '';
rows.forEach(function(r) {
html += '<tr>' +
'<td class="small">' + escape_html(format_date(r.entry_date)) + '</td>' +
'<td class="small">' + escape_html(r.period) + '</td>' +
'<td class="small"><span class="badge bg-secondary bg-opacity-10 text-secondary">' + escape_html(src_labels[r.source_type] || r.source_type) + '</span></td>' +
'<td class="small">' + (r.dept_code ? escape_html(r.dept_code) : '<span class="text-muted">—</span>') + '</td>' +
'<td class="small">' + escape_html(r.reference || '—') + '</td>' +
'<td class="small">' + escape_html(r.account_code + ' ' + r.account_name) + '</td>' +
'<td class="small"><span class="badge bg-info bg-opacity-10 text-info">' + escape_html(cat_labels[r.account_category] || r.account_category) + '</span></td>' +
'<td class="small text-muted">' + escape_html(r.description || '—') + '</td>' +
'<td class="text-end small">' + (parseFloat(r.debit) ? format_number(r.debit) : '—') + '</td>' +
'<td class="text-end small">' + (parseFloat(r.credit) ? format_number(r.credit) : '—') + '</td>' +
'</tr>';
});
document.getElementById('vat_tbody').innerHTML = html;
}
});
}
function export_report(format) {
if (export_data.length === 0) { bootbox.alert('No data to export. Run the report first.'); return; }
var from = document.getElementById('f_from').value;
var to = document.getElementById('f_to').value;
var filename = 'vat_report_' + from + '_' + to;
if (format === 'csv') {
alasql('SELECT * INTO CSV("' + filename + '.csv",{headers:true}) FROM ?', [export_data]);
} else if (format === 'xlsx') {
alasql('SELECT * INTO XLSX("' + filename + '.xlsx",{headers:true}) FROM ?', [export_data]);
} else if (format === 'pdf') {
var { jsPDF } = window.jspdf;
var doc = new jsPDF({ orientation: 'landscape' });
doc.setFontSize(11);
doc.text('VAT Report ' + from + ' to ' + to, 14, 14);
doc.autoTable({
startY: 20,
head: [['Date','Period','Type','Dept','Reference','Account','Category','Description','Debit','Credit']],
body: export_data.map(function(r){ return [r.date,r.period,r.type,r.department,r.reference,r.account,r.category,r.description,r.debit,r.credit]; }),
styles: { fontSize: 7 },
headStyles: { fillColor: [41,128,185] },
});
doc.save(filename + '.pdf');
}
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.8.2/jspdf.plugin.autotable.min.js"></script>
</body>
</html>