accounting modules
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../config.php';
|
||||
require '../include_header.php';
|
||||
?>
|
||||
|
||||
<body>
|
||||
<?php require '../include_topbar.php'; ?>
|
||||
<?php require '../include_sidebar_ac.php'; ?>
|
||||
|
||||
<!-- MAIN CONTENT -->
|
||||
<main id="content" class="content py-15">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Page header -->
|
||||
<div class="row mb-6">
|
||||
<div class="col-12">
|
||||
<h1 class="fs-3 mb-1">Accounting Dashboard</h1>
|
||||
<p class="mb-0 text-muted">Financial overview</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Placeholder -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-calculator fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">Accounting dashboard coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes_ac/ChartOfAccounts.php';
|
||||
|
||||
$coa = new ChartOfAccounts($pdo2, $company_id);
|
||||
$answer['output'] = $coa->getAll();
|
||||
$answer['success'] = 1;
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes_ac/ChartOfAccounts.php';
|
||||
|
||||
$coa = new ChartOfAccounts($pdo2, $company_id);
|
||||
$stats = $coa->getStats();
|
||||
$answer = array_merge(['success' => 1], $stats);
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes_ac/DepartmentManager.php';
|
||||
|
||||
$dept = new DepartmentManager($pdo2, $company_id);
|
||||
$answer['output'] = $dept->getAll();
|
||||
$answer['success'] = 1;
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes_ac/DepartmentManager.php';
|
||||
|
||||
$dept = new DepartmentManager($pdo2, $company_id);
|
||||
$stats = $dept->getStats();
|
||||
$answer = array_merge(['success' => 1], $stats);
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes_ac/ChartOfAccounts.php';
|
||||
|
||||
if (empty($data['account_code']) || empty($data['account_name']) || empty($data['account_type'])) {
|
||||
exit(json_encode(['message' => 'Account code, name, and type are required']));
|
||||
}
|
||||
|
||||
$coa = new ChartOfAccounts($pdo2, $company_id);
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
|
||||
if ($id) {
|
||||
$coa->update($id, $data);
|
||||
$answer['message'] = 'Account updated';
|
||||
} else {
|
||||
$coa->create($data);
|
||||
$answer['message'] = 'Account created';
|
||||
}
|
||||
|
||||
$answer['success'] = 1;
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes_ac/DepartmentManager.php';
|
||||
|
||||
if (empty($data['dept_code']) || empty($data['dept_name'])) {
|
||||
exit(json_encode(['message' => 'Department code and name are required']));
|
||||
}
|
||||
|
||||
$dept = new DepartmentManager($pdo2, $company_id);
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
|
||||
if ($id) {
|
||||
$dept->update($id, $data);
|
||||
$answer['message'] = 'Department updated';
|
||||
} else {
|
||||
$dept->create($data);
|
||||
$answer['message'] = 'Department created';
|
||||
}
|
||||
|
||||
$answer['success'] = 1;
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes_ac/PeriodManager.php';
|
||||
|
||||
$pm = new PeriodManager($pdo2, $company_id);
|
||||
$action = $data['action_type'] ?? '';
|
||||
|
||||
if ($action === 'create_year') {
|
||||
$year = (int)($data['year'] ?? date('Y'));
|
||||
if ($year < 2000 || $year > 2100) exit(json_encode(['message' => 'Invalid year']));
|
||||
$pm->createYear($year);
|
||||
$answer['message'] = "All 12 periods for {$year} created";
|
||||
|
||||
} elseif ($action === 'create_single') {
|
||||
$period = $data['period'] ?? '';
|
||||
if (!preg_match('/^\d{4}-(0[1-9]|1[0-2])$/', $period)) {
|
||||
exit(json_encode(['message' => 'Invalid period format (YYYY-MM)']));
|
||||
}
|
||||
$pm->create($period);
|
||||
$answer['message'] = "Period {$period} created";
|
||||
|
||||
} elseif ($action === 'open') {
|
||||
$pm->open((int)($data['id'] ?? 0));
|
||||
$answer['message'] = 'Period reopened';
|
||||
|
||||
} elseif ($action === 'close') {
|
||||
$pm->close((int)($data['id'] ?? 0));
|
||||
$answer['message'] = 'Period closed';
|
||||
|
||||
} elseif ($action === 'lock') {
|
||||
$pm->lock((int)($data['id'] ?? 0));
|
||||
$answer['message'] = 'Period locked';
|
||||
|
||||
} else {
|
||||
exit(json_encode(['message' => 'Unknown action']));
|
||||
}
|
||||
|
||||
$answer['success'] = 1;
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes_ac/PeriodManager.php';
|
||||
|
||||
$pm = new PeriodManager($pdo2, $company_id);
|
||||
$answer['output'] = $pm->getAll();
|
||||
$answer['success'] = 1;
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes_ac/PeriodManager.php';
|
||||
|
||||
$pm = new PeriodManager($pdo2, $company_id);
|
||||
$stats = $pm->getStats();
|
||||
$answer = array_merge(['success' => 1], $stats);
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes_ac/ChartOfAccounts.php';
|
||||
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
if (!$id) {
|
||||
exit(json_encode(['message' => 'Missing id']));
|
||||
}
|
||||
|
||||
$coa = new ChartOfAccounts($pdo2, $company_id);
|
||||
$coa->delete($id);
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Account removed';
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes_ac/DepartmentManager.php';
|
||||
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
if (!$id) exit(json_encode(['message' => 'Missing id']));
|
||||
|
||||
$dept = new DepartmentManager($pdo2, $company_id);
|
||||
$dept->delete($id);
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Department removed';
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes_ac/ChartOfAccounts.php';
|
||||
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
if (!$id) {
|
||||
exit(json_encode(['message' => 'Missing id']));
|
||||
}
|
||||
|
||||
$coa = new ChartOfAccounts($pdo2, $company_id);
|
||||
$row = $coa->getById($id);
|
||||
if (!$row) {
|
||||
exit(json_encode(['message' => 'Account not found']));
|
||||
}
|
||||
|
||||
$answer['output'] = $row;
|
||||
$answer['success'] = 1;
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes_ac/DepartmentManager.php';
|
||||
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
if (!$id) exit(json_encode(['message' => 'Missing id']));
|
||||
|
||||
$dept = new DepartmentManager($pdo2, $company_id);
|
||||
$row = $dept->getById($id);
|
||||
if (!$row) exit(json_encode(['message' => 'Department not found']));
|
||||
|
||||
$answer['output'] = $row;
|
||||
$answer['success'] = 1;
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">Balance Sheet</h1>
|
||||
<p class="mb-0 text-muted">Assets, liabilities, and equity</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-table fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">Balance Sheet coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,272 @@
|
||||
<?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>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 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 ac_account');
|
||||
alasql.tables.ac_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 ac_account ${where} ORDER BY account_code`);
|
||||
|
||||
var page = generate_pagination('ac_account', filtered.length);
|
||||
$('table#account_table tfoot').html(page);
|
||||
|
||||
if (filtered.length === 0) {
|
||||
$('table#account_table tbody').html(
|
||||
'<tr><td colspan="7" 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">${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>
|
||||
@@ -0,0 +1,214 @@
|
||||
<?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">Departments</h1>
|
||||
<p class="mb-0">GL department dimensions for journal entries</p>
|
||||
</div>
|
||||
<div>
|
||||
<a href="<?php echo $server_url?>accounting/manage_department.php" class="btn btn-primary">
|
||||
<i class="ti ti-plus me-1"></i>Add Department
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="row g-5 mb-5">
|
||||
<div class="col-xl-4 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-building fs-4"></i>
|
||||
</div>
|
||||
<div>
|
||||
<span>Total</span>
|
||||
<h2 class="fw-bold mb-0" id="stat_total">—</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-4 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-4 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-secondary bg-opacity-10 text-secondary rounded-2">
|
||||
<i class="ti ti-x fs-4"></i>
|
||||
</div>
|
||||
<div>
|
||||
<span>Inactive</span>
|
||||
<h2 class="fw-bold mb-0" id="stat_inactive">—</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 justify-content-end">
|
||||
<div class="position-relative" style="min-width:240px;">
|
||||
<input type="search" id="search_dept" 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="dept_table">
|
||||
<thead class="table-primary border-light">
|
||||
<tr>
|
||||
<th>Code</th>
|
||||
<th>Name</th>
|
||||
<th>Description</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 = '';
|
||||
|
||||
function retrieve_departments() {
|
||||
return ajax_request({
|
||||
url: '<?php echo $server_url?>accounting/api/engine/department.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'read',
|
||||
onSuccess: function(res) {
|
||||
alasql('CREATE TABLE IF NOT EXISTS ac_department');
|
||||
alasql.tables.ac_department.data = res.output || [];
|
||||
change_page(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function change_page(page_num) {
|
||||
var offset = (page_num - 1) * prop_limit;
|
||||
var kw = search_term.toLowerCase();
|
||||
|
||||
var filtered = kw
|
||||
? alasql(`SELECT * FROM ac_department WHERE LOWER(dept_code) LIKE '%${kw}%' OR LOWER(dept_name) LIKE '%${kw}%' ORDER BY dept_code`)
|
||||
: alasql(`SELECT * FROM ac_department ORDER BY dept_code`);
|
||||
|
||||
$('table#dept_table tfoot').html(generate_pagination('ac_department', filtered.length));
|
||||
|
||||
if (filtered.length === 0) {
|
||||
$('table#dept_table tbody').html('<tr><td colspan="5" class="text-center py-5 text-muted">No departments found.</td></tr>');
|
||||
return;
|
||||
}
|
||||
|
||||
var page_data = filtered.slice(offset, offset + prop_limit);
|
||||
var body = '';
|
||||
$.each(page_data, function(i, item) {
|
||||
body += `<tr>
|
||||
<td class="py-3 fw-semibold">${escape_html(item.dept_code)}</td>
|
||||
<td class="py-3">${escape_html(item.dept_name)}</td>
|
||||
<td class="py-3 text-muted">${item.description ? escape_html(item.description) : '—'}</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_department.php?id=${item.id}"><i class="ti ti-eye fs-5"></i></a>
|
||||
<a href="javascript:;" class="link-danger ms-2" onclick="remove_department(${item.id}, $(this))"><i class="ti ti-trash fs-5"></i></a>
|
||||
</td>
|
||||
</tr>`;
|
||||
});
|
||||
$('table#dept_table tbody').html(body);
|
||||
}
|
||||
|
||||
function remove_department(id, element) {
|
||||
return ajax_request({
|
||||
url: '<?php echo $server_url?>accounting/api/engine/remove_department.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/department_stats.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'read',
|
||||
onSuccess: function(res) {
|
||||
$('#stat_total').text(res.total || 0);
|
||||
$('#stat_active').text(res.active || 0);
|
||||
$('#stat_inactive').text(res.inactive || 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_dept').on('input', debounce(function() {
|
||||
search_term = $(this).val().trim();
|
||||
change_page(1);
|
||||
}, 200));
|
||||
|
||||
$(async function() {
|
||||
try {
|
||||
await retrieve_stats();
|
||||
await retrieve_departments();
|
||||
} catch(e) { console.log(e); }
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">GL Movement</h1>
|
||||
<p class="mb-0 text-muted">General ledger movement report</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-arrows-exchange fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">GL Movement coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,242 @@
|
||||
<?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">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="d-flex align-items-center mb-5 gap-3">
|
||||
<a href="<?php echo $server_url?>accounting/chart_of_accounts.php" class="btn btn-light btn-sm">
|
||||
<i class="ti ti-arrow-left me-1"></i>Back
|
||||
</a>
|
||||
<div>
|
||||
<h1 class="fs-3 mb-0">Manage Account</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="accountForm">
|
||||
<div class="col-lg-7">
|
||||
<div class="card">
|
||||
<div class="card-body p-6">
|
||||
|
||||
<input type="hidden" id="id">
|
||||
|
||||
<div class="row g-4">
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Account Code <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="account_code" placeholder="e.g. 1100" required>
|
||||
<div class="form-text">1xxx Asset · 2xxx Liability · 3xxx Equity · 4xxx Revenue · 5-9xxx Expense</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<label class="form-label">Account Name <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="account_name" placeholder="e.g. Cash at Bank" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Account Type <span class="text-danger">*</span></label>
|
||||
<select class="form-select" id="account_type" required>
|
||||
<option value="">— Auto-detected from code —</option>
|
||||
<option value="asset">Asset</option>
|
||||
<option value="liability">Liability</option>
|
||||
<option value="equity">Equity</option>
|
||||
<option value="revenue">Revenue</option>
|
||||
<option value="expense">Expense</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Account Level <span class="text-danger">*</span></label>
|
||||
<select class="form-select" id="is_posting" required>
|
||||
<option value="1">Item — used in journal entries</option>
|
||||
<option value="0">Parent — grouping / reports only</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12" id="parent_row">
|
||||
<label class="form-label">Parent Account</label>
|
||||
<select class="form-select" id="parent_code">
|
||||
<option value="">— None (top-level parent) —</option>
|
||||
</select>
|
||||
<div class="form-text">Only parent-level accounts of the same type are shown.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Status</label>
|
||||
<select class="form-select" id="status">
|
||||
<option value="1">Active</option>
|
||||
<option value="0">Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mt-5">
|
||||
<button class="btn btn-primary" onclick="manage_account();" id="btn_submit">Create Account</button>
|
||||
<button class="btn btn-secondary" onclick="reset_input('#accountForm');" id="btn_reset">Clear</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Guide card -->
|
||||
<div class="col-lg-5">
|
||||
<div class="card border-0 bg-light">
|
||||
<div class="card-body p-5">
|
||||
<h6 class="fw-semibold mb-3"><i class="ti ti-info-circle me-1"></i>Account Level Guide</h6>
|
||||
<p class="small mb-3">
|
||||
<strong>Parent</strong> accounts group items for reporting (e.g. <em>1000 Current Assets</em>).
|
||||
They cannot be used in journal entries.
|
||||
</p>
|
||||
<p class="small mb-3">
|
||||
<strong>Item</strong> accounts are the leaf-level codes posted in every journal entry
|
||||
(e.g. <em>1110 Cash at Bank</em>).
|
||||
</p>
|
||||
<hr class="my-3">
|
||||
<p class="small mb-1 fw-semibold">Standard ranges:</p>
|
||||
<table class="table table-sm small mb-0">
|
||||
<tbody>
|
||||
<tr><td>1xxx</td><td>Asset</td></tr>
|
||||
<tr><td>2xxx</td><td>Liability</td></tr>
|
||||
<tr><td>3xxx</td><td>Equity</td></tr>
|
||||
<tr><td>4xxx</td><td>Revenue</td></tr>
|
||||
<tr><td>5–9xxx</td><td>Expense</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
<script>
|
||||
var all_accounts = [];
|
||||
|
||||
// ── Auto-detect type from code prefix ────────────────────────────────
|
||||
function detect_type(code) {
|
||||
var first = parseInt((code || '').charAt(0));
|
||||
if (first === 1) return 'asset';
|
||||
if (first === 2) return 'liability';
|
||||
if (first === 3) return 'equity';
|
||||
if (first === 4) return 'revenue';
|
||||
if (first >= 5 && first <= 9) return 'expense';
|
||||
return '';
|
||||
}
|
||||
|
||||
$('#account_code').on('input', function() {
|
||||
var type = detect_type($(this).val().trim());
|
||||
if (type) {
|
||||
$('#account_type').val(type);
|
||||
refresh_parent_dropdown(type, null);
|
||||
}
|
||||
});
|
||||
|
||||
$('#account_type').on('change', function() {
|
||||
refresh_parent_dropdown($(this).val(), null);
|
||||
});
|
||||
|
||||
// Hide parent row when account level is set to Parent (is_posting=0)
|
||||
$('#is_posting').on('change', function() {
|
||||
if ($(this).val() == '0') {
|
||||
$('#parent_row').hide();
|
||||
$('#parent_code').val('');
|
||||
} else {
|
||||
$('#parent_row').show();
|
||||
}
|
||||
});
|
||||
|
||||
// ── Parent dropdown: filter to same type + is_posting=0 ─────────────
|
||||
function refresh_parent_dropdown(type, selected_code) {
|
||||
var opts = '<option value="">— None (top-level parent) —</option>';
|
||||
var parents = all_accounts.filter(function(a) {
|
||||
return a.is_posting == 0 && a.status == 1 && (!type || a.account_type === type);
|
||||
});
|
||||
$.each(parents, function(i, item) {
|
||||
var sel = (selected_code && selected_code == item.account_code) ? 'selected' : '';
|
||||
opts += `<option value="${escape_html(item.account_code)}" ${sel}>${escape_html(item.account_code)} — ${escape_html(item.account_name)}</option>`;
|
||||
});
|
||||
$('#parent_code').html(opts);
|
||||
}
|
||||
|
||||
function load_all_accounts(selected_parent, selected_type) {
|
||||
return ajax_request({
|
||||
url: '<?php echo $server_url?>accounting/api/engine/account.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'read',
|
||||
onSuccess: function(res) {
|
||||
all_accounts = res.output || [];
|
||||
refresh_parent_dropdown(selected_type, selected_parent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function manage_account() {
|
||||
return ajax_request({
|
||||
url: '<?php echo $server_url?>accounting/api/engine/manage_account.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 1,
|
||||
action: 'manage',
|
||||
onSuccess: function() {
|
||||
window.location.href = '<?php echo $server_url?>accounting/chart_of_accounts.php';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function retrieve_for_edit() {
|
||||
<?php if (empty($_GET['id'])): ?>
|
||||
return false;
|
||||
<?php endif; ?>
|
||||
|
||||
return ajax_request({
|
||||
url: '<?php echo $server_url?>accounting/api/engine/retrieve_account.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'read',
|
||||
data: { id: <?php echo (int)$_GET['id']; ?> },
|
||||
onSuccess: function(res) {
|
||||
var item = res.output;
|
||||
$('#id').val(item.id);
|
||||
$('#account_code').val(item.account_code);
|
||||
$('#account_name').val(item.account_name);
|
||||
$('#account_type').val(item.account_type);
|
||||
$('#is_posting').val(item.is_posting);
|
||||
$('#status').val(item.status);
|
||||
if (item.is_posting == 0) $('#parent_row').hide();
|
||||
load_all_accounts(item.parent_code, item.account_type);
|
||||
$('#btn_submit').text('Update Account');
|
||||
$('#btn_reset').hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(async function() {
|
||||
try {
|
||||
<?php if (empty($_GET['id'])): ?>
|
||||
await load_all_accounts(null, null);
|
||||
<?php else: ?>
|
||||
await retrieve_for_edit();
|
||||
<?php endif; ?>
|
||||
} catch(e) { console.log(e); }
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,112 @@
|
||||
<?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">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="d-flex align-items-center mb-5 gap-3">
|
||||
<a href="<?php echo $server_url?>accounting/departments.php" class="btn btn-light btn-sm">
|
||||
<i class="ti ti-arrow-left me-1"></i>Back
|
||||
</a>
|
||||
<h1 class="fs-3 mb-0">Manage Department</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="deptForm">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-body p-6">
|
||||
|
||||
<input type="hidden" id="id">
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Department Code <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="dept_code" placeholder="e.g. HQ" required>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<label class="form-label">Department Name <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="dept_name" placeholder="e.g. Head Office" required>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Description</label>
|
||||
<textarea class="form-control" id="description" rows="3" placeholder="Optional description"></textarea>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Status</label>
|
||||
<select class="form-select" id="status">
|
||||
<option value="1">Active</option>
|
||||
<option value="0">Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mt-5">
|
||||
<button class="btn btn-primary" onclick="manage_department();" id="btn_submit">Create Department</button>
|
||||
<button class="btn btn-secondary" onclick="reset_input('#deptForm');" id="btn_reset">Clear</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
<script>
|
||||
function manage_department() {
|
||||
return ajax_request({
|
||||
url: '<?php echo $server_url?>accounting/api/engine/manage_department.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 1,
|
||||
action: 'manage',
|
||||
onSuccess: function() {
|
||||
window.location.href = '<?php echo $server_url?>accounting/departments.php';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function retrieve_for_edit() {
|
||||
<?php if (empty($_GET['id'])): ?>
|
||||
return false;
|
||||
<?php endif; ?>
|
||||
|
||||
return ajax_request({
|
||||
url: '<?php echo $server_url?>accounting/api/engine/retrieve_department.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'read',
|
||||
data: { id: <?php echo (int)$_GET['id']; ?> },
|
||||
onSuccess: function(res) {
|
||||
var item = res.output;
|
||||
$('#id').val(item.id);
|
||||
$('#dept_code').val(item.dept_code);
|
||||
$('#dept_name').val(item.dept_name);
|
||||
$('#description').val(item.description);
|
||||
$('#status').val(item.status);
|
||||
$('#btn_submit').text('Update Department');
|
||||
$('#btn_reset').hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(async function() {
|
||||
try { await retrieve_for_edit(); } catch(e) { console.log(e); }
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,274 @@
|
||||
<?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">Accounting Periods</h1>
|
||||
<p class="mb-0">Open, close, and lock monthly accounting periods</p>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-outline-primary" onclick="show_create_year_modal();">
|
||||
<i class="ti ti-calendar-plus me-1"></i>Generate Year
|
||||
</button>
|
||||
<button class="btn btn-primary" onclick="show_create_modal();">
|
||||
<i class="ti ti-plus me-1"></i>Add Period
|
||||
</button>
|
||||
</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-calendar fs-4"></i>
|
||||
</div>
|
||||
<div><span>Total</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-lock-open fs-4"></i>
|
||||
</div>
|
||||
<div><span>Open</span><h2 class="fw-bold mb-0" id="stat_open">—</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-lock fs-4"></i>
|
||||
</div>
|
||||
<div><span>Closed</span><h2 class="fw-bold mb-0" id="stat_closed">—</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-danger bg-opacity-10 text-danger rounded-2">
|
||||
<i class="ti ti-lock-filled fs-4"></i>
|
||||
</div>
|
||||
<div><span>Locked</span><h2 class="fw-bold mb-0" id="stat_locked">—</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="table-responsive">
|
||||
<table class="table mb-0 text-nowrap table-hover table-centered" id="period_table">
|
||||
<thead class="table-primary border-light">
|
||||
<tr>
|
||||
<th>Period</th>
|
||||
<th>Status</th>
|
||||
<th>Closed At</th>
|
||||
<th>Locked At</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Add Single Period Modal -->
|
||||
<div class="modal fade" id="createPeriodModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" style="max-width:340px;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h6 class="modal-title">Add Period</h6>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<label class="form-label">Period (YYYY-MM)</label>
|
||||
<input type="month" class="form-control" id="new_period">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button class="btn btn-primary" onclick="create_single();">Create</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Generate Year Modal -->
|
||||
<div class="modal fade" id="createYearModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" style="max-width:340px;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h6 class="modal-title">Generate Year Periods</h6>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<label class="form-label">Year</label>
|
||||
<input type="number" class="form-control" id="new_year" value="<?php echo date('Y'); ?>" min="2000" max="2100">
|
||||
<div class="form-text">Creates all 12 months for the selected year (skips existing ones).</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button class="btn btn-primary" onclick="create_year();">Generate</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
<script>
|
||||
var status_badge = {
|
||||
open: '<span class="badge bg-success">Open</span>',
|
||||
closed: '<span class="badge bg-warning text-dark">Closed</span>',
|
||||
locked: '<span class="badge bg-danger">Locked</span>',
|
||||
};
|
||||
|
||||
function retrieve_periods() {
|
||||
return ajax_request({
|
||||
url: '<?php echo $server_url?>accounting/api/engine/period.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'read',
|
||||
onSuccess: function(res) {
|
||||
var rows = res.output || [];
|
||||
if (rows.length === 0) {
|
||||
$('table#period_table tbody').html('<tr><td colspan="5" class="text-center py-5 text-muted">No periods yet. Use Generate Year to create them.</td></tr>');
|
||||
return;
|
||||
}
|
||||
var body = '';
|
||||
$.each(rows, function(i, item) {
|
||||
var actions = '';
|
||||
if (item.status === 'open') {
|
||||
actions = `<button class="btn btn-sm btn-outline-warning" onclick="do_action('close', ${item.id})">Close</button>`;
|
||||
} else if (item.status === 'closed') {
|
||||
actions = `<button class="btn btn-sm btn-outline-success me-1" onclick="do_action('open', ${item.id})">Reopen</button>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="do_action('lock', ${item.id})">Lock</button>`;
|
||||
} else {
|
||||
actions = '<span class="text-muted small">Permanent</span>';
|
||||
}
|
||||
body += `<tr>
|
||||
<td class="py-3 fw-semibold">${escape_html(item.period)}</td>
|
||||
<td class="py-3">${status_badge[item.status] || item.status}</td>
|
||||
<td class="py-3 text-muted small">${item.closed_at || '—'}</td>
|
||||
<td class="py-3 text-muted small">${item.locked_at || '—'}</td>
|
||||
<td class="py-3">${actions}</td>
|
||||
</tr>`;
|
||||
});
|
||||
$('table#period_table tbody').html(body);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function do_action(action_type, id) {
|
||||
return ajax_request({
|
||||
url: '<?php echo $server_url?>accounting/api/engine/manage_period.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'manage',
|
||||
data: { action_type: action_type, id: id },
|
||||
onSuccess: function() {
|
||||
retrieve_periods();
|
||||
retrieve_stats();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function create_single() {
|
||||
var period = $('#new_period').val();
|
||||
if (!period) return;
|
||||
ajax_request({
|
||||
url: '<?php echo $server_url?>accounting/api/engine/manage_period.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'manage',
|
||||
data: { action_type: 'create_single', period: period },
|
||||
onSuccess: function() {
|
||||
$('#createPeriodModal').modal('hide');
|
||||
retrieve_periods();
|
||||
retrieve_stats();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function create_year() {
|
||||
var year = $('#new_year').val();
|
||||
if (!year) return;
|
||||
ajax_request({
|
||||
url: '<?php echo $server_url?>accounting/api/engine/manage_period.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'manage',
|
||||
data: { action_type: 'create_year', year: year },
|
||||
onSuccess: function() {
|
||||
$('#createYearModal').modal('hide');
|
||||
retrieve_periods();
|
||||
retrieve_stats();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function retrieve_stats() {
|
||||
return ajax_request({
|
||||
url: '<?php echo $server_url?>accounting/api/engine/period_stats.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'read',
|
||||
onSuccess: function(res) {
|
||||
$('#stat_total').text(res.total || 0);
|
||||
$('#stat_open').text(res.open_count || 0);
|
||||
$('#stat_closed').text(res.closed_count || 0);
|
||||
$('#stat_locked').text(res.locked_count || 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function show_create_modal() { $('#new_period').val(''); $('#createPeriodModal').modal('show'); }
|
||||
function show_create_year_modal() { $('#createYearModal').modal('show'); }
|
||||
|
||||
$(async function() {
|
||||
try {
|
||||
await retrieve_stats();
|
||||
await retrieve_periods();
|
||||
} catch(e) { console.log(e); }
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">P&L Statement</h1>
|
||||
<p class="mb-0 text-muted">Profit and loss statement</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-chart-line fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">P&L Statement coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">Trial Balance</h1>
|
||||
<p class="mb-0 text-muted">Period trial balance report</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-scale fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">Trial Balance coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">VAT Report</h1>
|
||||
<p class="mb-0 text-muted">Value added tax report</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-receipt-tax fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">VAT Report coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -264,13 +264,27 @@ class OrderManager {
|
||||
if (!$row) {
|
||||
throw new Exception("Order not found.");
|
||||
}
|
||||
if ((int)$row['status'] !== 0) {
|
||||
throw new Exception("Only draft orders can be edited.");
|
||||
$cur_status = (int)$row['status'];
|
||||
if ($cur_status !== 0 && $cur_status !== -2) {
|
||||
throw new Exception("Only draft or pending orders can be edited.");
|
||||
}
|
||||
|
||||
$log = json_decode($row['log'] ?? '[]', true) ?: [];
|
||||
$log[] = $logging;
|
||||
|
||||
// Auto-promote Pending → Draft when all items have a warehouse assigned
|
||||
$new_status = $cur_status;
|
||||
if ($cur_status === -2) {
|
||||
$all_have_warehouse = !empty($items) && array_reduce(
|
||||
$items,
|
||||
fn($ok, $item) => $ok && (int)($item['warehouse_id'] ?? 0) > 0,
|
||||
true
|
||||
);
|
||||
if ($all_have_warehouse) {
|
||||
$new_status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$this->pdo->prepare(
|
||||
"UPDATE td_order SET
|
||||
contact_id = :contact_id,
|
||||
@@ -283,6 +297,7 @@ class OrderManager {
|
||||
shipping_tracking_number = :shipping_tracking_number,
|
||||
grand_total = :grand_total,
|
||||
notes = :notes,
|
||||
status = :status,
|
||||
`log` = :log
|
||||
WHERE id = :id AND company_id = :company_id"
|
||||
)->execute([
|
||||
@@ -296,6 +311,7 @@ class OrderManager {
|
||||
':shipping_tracking_number' => $tracking_no,
|
||||
':grand_total' => $grand_total,
|
||||
':notes' => $data['notes'] ?? '',
|
||||
':status' => $new_status,
|
||||
':log' => json_encode($log),
|
||||
':id' => $id,
|
||||
':company_id' => $this->company_id,
|
||||
@@ -305,23 +321,28 @@ class OrderManager {
|
||||
|
||||
} else {
|
||||
|
||||
$log = [$logging];
|
||||
$log = [$logging];
|
||||
$quotation_id = (int)($data['quotation_id'] ?? 0);
|
||||
// Quotation-converted orders start as Pending (-2); direct orders as Draft (0)
|
||||
$init_status = $quotation_id > 0 ? -2 : 0;
|
||||
|
||||
$this->pdo->prepare(
|
||||
"INSERT INTO td_order
|
||||
(company_id, uuid, order_number, contact_id, order_date,
|
||||
(company_id, uuid, quotation_id, order_number, contact_id, order_date,
|
||||
status, payment_status, subtotal, discount, tax,
|
||||
shipping_fee, shipping_tracking_number, grand_total, items, notes, `log`, created_at)
|
||||
VALUES
|
||||
(:company_id, :uuid, :order_number, :contact_id, :order_date,
|
||||
0, 0, :subtotal, :discount, :tax,
|
||||
(:company_id, :uuid, :quotation_id, :order_number, :contact_id, :order_date,
|
||||
:status, 0, :subtotal, :discount, :tax,
|
||||
:shipping_fee, :shipping_tracking_number, :grand_total, :items, :notes, :log, :created_at)"
|
||||
)->execute([
|
||||
':company_id' => $this->company_id,
|
||||
':uuid' => bin2hex(random_bytes(16)),
|
||||
':quotation_id' => $quotation_id,
|
||||
':order_number' => $this->generateOrderNumber(),
|
||||
':contact_id' => (int)($data['contact_id'] ?? 0),
|
||||
':order_date' => $data['order_date'] ?? date('Y-m-d'),
|
||||
':status' => $init_status,
|
||||
':subtotal' => $subtotal,
|
||||
':discount' => $discount,
|
||||
':tax' => $tax,
|
||||
@@ -539,6 +560,7 @@ class OrderManager {
|
||||
"Cannot cancel an order that is processing or completed."
|
||||
);
|
||||
}
|
||||
// Pending orders (-2) have no stock rows — skip the stock reversal loop below
|
||||
|
||||
// ── Guard: no active invoice ──────────────────────────────────────
|
||||
$sth = $this->pdo->prepare(
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* PostingManager
|
||||
*
|
||||
* The ONLY bridge between WMS and the accounting engine.
|
||||
* WMS API endpoints call methods here — nothing in WMS imports
|
||||
* accounting classes directly.
|
||||
*
|
||||
* Usage in a WMS API endpoint (add at the end, after business doc saves):
|
||||
* require_once '../../../assets/utils/classes/PostingManager.php';
|
||||
* (new PostingManager($pdo2, $company_id))->postInvoice($invoice_id);
|
||||
*
|
||||
* If posting fails it logs silently — the business document is already saved.
|
||||
*/
|
||||
class PostingManager
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: postInvoice(int $invoice_id): void
|
||||
// TODO: voidInvoice(int $invoice_id): void
|
||||
|
||||
// TODO: postPurchaseReceive(int $po_id): void
|
||||
// TODO: voidPurchaseReceive(int $po_id): void
|
||||
|
||||
// TODO: postReceipt(int $receipt_id): void
|
||||
// TODO: voidReceipt(int $receipt_id): void
|
||||
|
||||
// TODO: postPayment(int $payment_id): void
|
||||
// TODO: voidPayment(int $payment_id): void
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* AccountingEngine
|
||||
*
|
||||
* Core double-entry validation and rules.
|
||||
* Ensures every journal entry balances (total debit = total credit).
|
||||
* All other accounting classes depend on this for validation.
|
||||
*/
|
||||
class AccountingEngine
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: validate journal lines balance (sum debit = sum credit)
|
||||
// TODO: validate account codes exist and are posting accounts
|
||||
// TODO: validate period is open
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
class ChartOfAccounts
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
public function getAll(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_account
|
||||
WHERE company_id = :cid
|
||||
ORDER BY account_code ASC"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId]);
|
||||
return $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function getByCode(string $account_code): ?array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_account
|
||||
WHERE company_id = :cid AND account_code = :code
|
||||
LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':code' => $account_code]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
public function getByType(string $account_type): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_account
|
||||
WHERE company_id = :cid AND account_type = :type AND status = 1
|
||||
ORDER BY account_code ASC"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':type' => $account_type]);
|
||||
return $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function getById(int $id): ?array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_account
|
||||
WHERE company_id = :cid AND id = :id
|
||||
LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
public function create(array $data): int
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"INSERT INTO ac_account
|
||||
(company_id, account_code, account_name, account_type, parent_code, is_posting, status)
|
||||
VALUES
|
||||
(:cid, :code, :name, :type, :parent, :posting, :status)"
|
||||
);
|
||||
$sth->execute([
|
||||
':cid' => $this->companyId,
|
||||
':code' => trim($data['account_code']),
|
||||
':name' => trim($data['account_name']),
|
||||
':type' => $data['account_type'],
|
||||
':parent' => !empty($data['parent_code']) ? trim($data['parent_code']) : null,
|
||||
':posting' => (int)($data['is_posting'] ?? 1),
|
||||
':status' => (int)($data['status'] ?? 1),
|
||||
]);
|
||||
return (int)$this->pdo->lastInsertId();
|
||||
}
|
||||
|
||||
public function update(int $id, array $data): void
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"UPDATE ac_account SET
|
||||
account_code = :code,
|
||||
account_name = :name,
|
||||
account_type = :type,
|
||||
parent_code = :parent,
|
||||
is_posting = :posting,
|
||||
status = :status
|
||||
WHERE company_id = :cid AND id = :id"
|
||||
);
|
||||
$sth->execute([
|
||||
':cid' => $this->companyId,
|
||||
':id' => $id,
|
||||
':code' => trim($data['account_code']),
|
||||
':name' => trim($data['account_name']),
|
||||
':type' => $data['account_type'],
|
||||
':parent' => !empty($data['parent_code']) ? trim($data['parent_code']) : null,
|
||||
':posting' => (int)($data['is_posting'] ?? 1),
|
||||
':status' => (int)($data['status'] ?? 1),
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete(int $id): void
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"UPDATE ac_account SET status = 0
|
||||
WHERE company_id = :cid AND id = :id"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
}
|
||||
|
||||
public function isPostingAccount(string $account_code): bool
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT is_posting FROM ac_account
|
||||
WHERE company_id = :cid AND account_code = :code AND status = 1
|
||||
LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':code' => $account_code]);
|
||||
return (bool)$sth->fetchColumn();
|
||||
}
|
||||
|
||||
public function getStats(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT
|
||||
COUNT(*) AS total,
|
||||
SUM(status = 1) AS active,
|
||||
SUM(status = 0) AS inactive,
|
||||
SUM(account_type = 'asset') AS asset,
|
||||
SUM(account_type = 'liability') AS liability,
|
||||
SUM(account_type = 'equity') AS equity,
|
||||
SUM(account_type = 'revenue') AS revenue,
|
||||
SUM(account_type = 'expense') AS expense
|
||||
FROM ac_account
|
||||
WHERE company_id = :cid"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId]);
|
||||
return $sth->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
class DepartmentManager
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
public function getAll(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_department
|
||||
WHERE company_id = :cid
|
||||
ORDER BY dept_code ASC"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId]);
|
||||
return $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function getById(int $id): ?array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_department
|
||||
WHERE company_id = :cid AND id = :id LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
public function create(array $data): int
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"INSERT INTO ac_department (company_id, dept_code, dept_name, description, status)
|
||||
VALUES (:cid, :code, :name, :desc, :status)"
|
||||
);
|
||||
$sth->execute([
|
||||
':cid' => $this->companyId,
|
||||
':code' => trim($data['dept_code']),
|
||||
':name' => trim($data['dept_name']),
|
||||
':desc' => $data['description'] ?? null,
|
||||
':status' => (int)($data['status'] ?? 1),
|
||||
]);
|
||||
return (int)$this->pdo->lastInsertId();
|
||||
}
|
||||
|
||||
public function update(int $id, array $data): void
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"UPDATE ac_department SET
|
||||
dept_code = :code,
|
||||
dept_name = :name,
|
||||
description = :desc,
|
||||
status = :status
|
||||
WHERE company_id = :cid AND id = :id"
|
||||
);
|
||||
$sth->execute([
|
||||
':cid' => $this->companyId,
|
||||
':id' => $id,
|
||||
':code' => trim($data['dept_code']),
|
||||
':name' => trim($data['dept_name']),
|
||||
':desc' => $data['description'] ?? null,
|
||||
':status' => (int)($data['status'] ?? 1),
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete(int $id): void
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"UPDATE ac_department SET status = 0
|
||||
WHERE company_id = :cid AND id = :id"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
}
|
||||
|
||||
public function getStats(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT COUNT(*) AS total,
|
||||
SUM(status = 1) AS active,
|
||||
SUM(status = 0) AS inactive
|
||||
FROM ac_department WHERE company_id = :cid"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId]);
|
||||
return $sth->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* FinancialReports
|
||||
*
|
||||
* Generates all financial statements from ac_balance + ac_journal_line.
|
||||
* Read-only — no writes.
|
||||
*/
|
||||
class FinancialReports
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: getTrialBalance(string $from_period, string $to_period, ?int $dept_id): array
|
||||
// TODO: getProfitAndLoss(string $from_period, string $to_period, ?int $dept_id): array
|
||||
// TODO: getBalanceSheet(string $as_of_period, ?int $dept_id): array
|
||||
// TODO: getGLMovement(string $account_code, string $from_period, string $to_period): array
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* JournalManager
|
||||
*
|
||||
* Create, read, and void journal entries in ac_journal + ac_journal_line.
|
||||
* Updates ac_balance after each post.
|
||||
* Never called directly by WMS — goes through PostingManager.
|
||||
*/
|
||||
class JournalManager
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: create(array $header, array $lines): int — returns journal_id
|
||||
// TODO: void(int $journal_id): void
|
||||
// TODO: getBySource(string $source_type, int $source_id): ?array
|
||||
// TODO: getById(int $journal_id): ?array
|
||||
// TODO: updateBalance(string $period, string $account_code, ?int $dept_id, float $debit, float $credit): void
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
class PeriodManager
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
public function getAll(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_period
|
||||
WHERE company_id = :cid
|
||||
ORDER BY period DESC"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId]);
|
||||
return $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function getById(int $id): ?array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_period WHERE company_id = :cid AND id = :id LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
public function isOpen(string $period): bool
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT status FROM ac_period
|
||||
WHERE company_id = :cid AND period = :period LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':period' => $period]);
|
||||
$status = $sth->fetchColumn();
|
||||
return $status === 'open';
|
||||
}
|
||||
|
||||
// Get existing period or auto-create it as open
|
||||
public function getOrCreate(string $period): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_period WHERE company_id = :cid AND period = :period LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':period' => $period]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
if ($row) return $row;
|
||||
|
||||
$this->pdo->prepare(
|
||||
"INSERT INTO ac_period (company_id, period, status) VALUES (:cid, :period, 'open')"
|
||||
)->execute([':cid' => $this->companyId, ':period' => $period]);
|
||||
|
||||
return $this->getOrCreate($period);
|
||||
}
|
||||
|
||||
// Create a single period (manual)
|
||||
public function create(string $period): void
|
||||
{
|
||||
$this->pdo->prepare(
|
||||
"INSERT IGNORE INTO ac_period (company_id, period, status) VALUES (:cid, :period, 'open')"
|
||||
)->execute([':cid' => $this->companyId, ':period' => $period]);
|
||||
}
|
||||
|
||||
// Bulk-create all 12 months of a year
|
||||
public function createYear(int $year): void
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"INSERT IGNORE INTO ac_period (company_id, period, status) VALUES (:cid, :period, 'open')"
|
||||
);
|
||||
for ($m = 1; $m <= 12; $m++) {
|
||||
$sth->execute([
|
||||
':cid' => $this->companyId,
|
||||
':period' => sprintf('%04d-%02d', $year, $m),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function open(int $id): void
|
||||
{
|
||||
$this->pdo->prepare(
|
||||
"UPDATE ac_period SET status = 'open', closed_at = NULL
|
||||
WHERE company_id = :cid AND id = :id AND status = 'closed'"
|
||||
)->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
}
|
||||
|
||||
public function close(int $id): void
|
||||
{
|
||||
$this->pdo->prepare(
|
||||
"UPDATE ac_period SET status = 'closed', closed_at = NOW()
|
||||
WHERE company_id = :cid AND id = :id AND status = 'open'"
|
||||
)->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
}
|
||||
|
||||
public function lock(int $id): void
|
||||
{
|
||||
$this->pdo->prepare(
|
||||
"UPDATE ac_period SET status = 'locked', locked_at = NOW()
|
||||
WHERE company_id = :cid AND id = :id AND status = 'closed'"
|
||||
)->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
}
|
||||
|
||||
public function getStats(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT COUNT(*) AS total,
|
||||
SUM(status = 'open') AS open_count,
|
||||
SUM(status = 'closed') AS closed_count,
|
||||
SUM(status = 'locked') AS locked_count
|
||||
FROM ac_period WHERE company_id = :cid"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId]);
|
||||
return $sth->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* TaxReportManager
|
||||
*
|
||||
* Generates VAT and withholding tax reports from journal lines
|
||||
* tagged to tax accounts (output VAT, input VAT, WHT).
|
||||
* Read-only — no writes.
|
||||
*/
|
||||
class TaxReportManager
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: getOutputVAT(string $from_period, string $to_period): array
|
||||
// TODO: getInputVAT(string $from_period, string $to_period): array
|
||||
// TODO: getWithholdingTax(string $from_period, string $to_period): array
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* InvoicePosting
|
||||
*
|
||||
* Builds the journal entry for a posted sales invoice.
|
||||
* DR: Accounts Receivable
|
||||
* CR: Revenue + Output VAT
|
||||
*/
|
||||
class InvoicePosting
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: build(int $invoice_id): array — returns [header, lines] ready for JournalManager
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* PaymentPosting
|
||||
*
|
||||
* Builds the journal entry when a supplier payment is recorded.
|
||||
* DR: Accounts Payable
|
||||
* CR: Bank / Cash
|
||||
*/
|
||||
class PaymentPosting
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: build(int $payment_id): array — returns [header, lines] ready for JournalManager
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* PurchasePosting
|
||||
*
|
||||
* Builds the journal entry when a Purchase Order is received (GRN).
|
||||
* DR: Inventory
|
||||
* CR: Accounts Payable
|
||||
*/
|
||||
class PurchasePosting
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: build(int $po_id): array — returns [header, lines] ready for JournalManager
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* ReceiptPosting
|
||||
*
|
||||
* Builds the journal entry when a customer receipt is recorded.
|
||||
* DR: Bank / Cash
|
||||
* CR: Accounts Receivable
|
||||
*/
|
||||
class ReceiptPosting
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: build(int $receipt_id): array — returns [header, lines] ready for JournalManager
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">Payments</h1>
|
||||
<p class="mb-0 text-muted">Payments to suppliers</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-credit-card fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">Payments coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">Purchase Invoices</h1>
|
||||
<p class="mb-0 text-muted">Supplier invoices</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-file-invoice fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">Purchase Invoices coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">Purchase Orders</h1>
|
||||
<p class="mb-0 text-muted">Purchase order management</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-clipboard-list fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">Purchase Orders coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">Purchase Requests</h1>
|
||||
<p class="mb-0 text-muted">Internal purchase requests</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-notes fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">Purchase Requests coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">Payments</h1>
|
||||
<p class="mb-0 text-muted">Cash and bank payments</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-credit-card fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">Payments coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">Receipts</h1>
|
||||
<p class="mb-0 text-muted">Cash and bank receipts</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-cash fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">Receipts coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +1,20 @@
|
||||
<?php
|
||||
$current_page = basename($_SERVER['PHP_SELF']);
|
||||
$master_role = $_SESSION['login_role'] ?? 'viewer';
|
||||
$cp = $_SERVER['PHP_SELF'];
|
||||
function master_active(...$frags) {
|
||||
global $cp;
|
||||
foreach ($frags as $f) { if (str_contains($cp, $f)) return 'active'; }
|
||||
return '';
|
||||
}
|
||||
$master_role = $_SESSION['login_role'] ?? 'viewer';
|
||||
$master_can_admin = in_array($master_role, ['owner', 'admin'], true);
|
||||
$master_app_access = $_SESSION['login_app_access'] ?? 'wms';
|
||||
$master_show_accounting = in_array($master_app_access, ['all', 'accounting']);
|
||||
$master_back_url = ($_SESSION['login_current_app'] ?? 'wms') === 'accounting'
|
||||
? $server_url . 'ac_dashboard/index.php'
|
||||
: $server_url . 'dashboard/index.php';
|
||||
$master_back_label = ($_SESSION['login_current_app'] ?? 'wms') === 'accounting'
|
||||
? 'Back to Accounting'
|
||||
: 'Back to WMS';
|
||||
?>
|
||||
|
||||
<!-- SIDEBAR -->
|
||||
@@ -23,7 +36,7 @@
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo in_array($current_page, ['product.php', 'manage_product.php', 'manage_category.php']) ? 'active' : ''; ?>"
|
||||
<a class="nav-link <?php echo master_active('/inventory/product', '/inventory/manage_product', '/inventory/manage_category'); ?>"
|
||||
href="<?php echo $server_url?>inventory/product.php">
|
||||
<i class="ti ti-category"></i>
|
||||
<span class="nav-text">Products</span>
|
||||
@@ -32,7 +45,7 @@
|
||||
|
||||
<?php if ($master_can_admin): ?>
|
||||
<li>
|
||||
<a class="nav-link <?php echo in_array($current_page, ['warehouse.php', 'manage_warehouse.php', 'manage_storage.php']) ? 'active' : ''; ?>"
|
||||
<a class="nav-link <?php echo master_active('/inventory/warehouse', '/inventory/manage_warehouse', '/inventory/manage_storage'); ?>"
|
||||
href="<?php echo $server_url?>inventory/warehouse.php">
|
||||
<i class="ti ti-map-pin"></i>
|
||||
<span class="nav-text">Warehouse Location</span>
|
||||
@@ -41,22 +54,53 @@
|
||||
<?php endif; ?>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo in_array($current_page, ['contact.php', 'manage_contact.php', 'manage_contact_type.php']) ? 'active' : ''; ?>"
|
||||
<a class="nav-link <?php echo master_active('/contact/'); ?>"
|
||||
href="<?php echo $server_url?>contact/contact.php">
|
||||
<i class="ti ti-users"></i>
|
||||
<span class="nav-text">Contact</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Accounting -->
|
||||
<?php if ($master_show_accounting): ?>
|
||||
<li class="nav-text-space mt-2">
|
||||
<small class="nav-text text-muted">Accounting</small>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo master_active('/accounting/chart_of_accounts', '/accounting/manage_account'); ?>"
|
||||
href="<?php echo $server_url?>accounting/chart_of_accounts.php">
|
||||
<i class="ti ti-list-tree"></i>
|
||||
<span class="nav-text">Chart of Accounts</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo master_active('/accounting/departments', '/accounting/manage_department'); ?>"
|
||||
href="<?php echo $server_url?>accounting/departments.php">
|
||||
<i class="ti ti-building"></i>
|
||||
<span class="nav-text">Departments</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo master_active('/accounting/periods'); ?>"
|
||||
href="<?php echo $server_url?>accounting/periods.php">
|
||||
<i class="ti ti-calendar"></i>
|
||||
<span class="nav-text">Accounting Periods</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Navigation -->
|
||||
<li class="nav-text-space mt-2">
|
||||
<small class="nav-text text-muted">Navigation</small>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>dashboard/index.php">
|
||||
<a class="nav-link" href="<?php echo $master_back_url; ?>">
|
||||
<i class="ti ti-arrow-left"></i>
|
||||
<span class="nav-text">Back to App</span>
|
||||
<span class="nav-text"><?php echo $master_back_label; ?></span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
+24
-18
@@ -1,6 +1,12 @@
|
||||
<?php
|
||||
$sidebar_role = $_SESSION['login_role'] ?? 'viewer';
|
||||
$sidebar_role = $_SESSION['login_role'] ?? 'viewer';
|
||||
$sidebar_can_staff = in_array($sidebar_role, ['owner', 'admin', 'staff'], true);
|
||||
$cp = $_SERVER['PHP_SELF'];
|
||||
function wms_active(...$frags) {
|
||||
global $cp;
|
||||
foreach ($frags as $f) { if (str_contains($cp, $f)) return 'active'; }
|
||||
return '';
|
||||
}
|
||||
?>
|
||||
<!-- SIDEBAR -->
|
||||
<aside id="sidebar" class="sidebar overflow-y-auto">
|
||||
@@ -21,14 +27,14 @@
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link active" href="<?php echo $server_url?>dashboard/index.php">
|
||||
<a class="nav-link <?php echo wms_active('/dashboard/index'); ?>" href="<?php echo $server_url?>dashboard/index.php">
|
||||
<i class="ti ti-home"></i>
|
||||
<span class="nav-text">Dashboard</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>dashboard/low_stock_products.php">
|
||||
<a class="nav-link <?php echo wms_active('low_stock'); ?>" href="<?php echo $server_url?>dashboard/low_stock_products.php">
|
||||
<i class="ti ti-alert-triangle"></i>
|
||||
<span class="nav-text">Low Stock</span>
|
||||
</a>
|
||||
@@ -40,7 +46,7 @@
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>ics/stock_overview.php">
|
||||
<a class="nav-link <?php echo wms_active('stock_overview'); ?>" href="<?php echo $server_url?>ics/stock_overview.php">
|
||||
<i class="ti ti-clipboard"></i>
|
||||
<span class="nav-text">Stock Overview</span>
|
||||
</a>
|
||||
@@ -48,35 +54,35 @@
|
||||
|
||||
<?php if ($sidebar_can_staff): ?>
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>ics/stock_in.php">
|
||||
<a class="nav-link <?php echo wms_active('stock_in'); ?>" href="<?php echo $server_url?>ics/stock_in.php">
|
||||
<i class="ti ti-package-import"></i>
|
||||
<span class="nav-text">Stock In</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>ics/stock_out.php">
|
||||
<a class="nav-link <?php echo wms_active('stock_out'); ?>" href="<?php echo $server_url?>ics/stock_out.php">
|
||||
<i class="ti ti-package-export"></i>
|
||||
<span class="nav-text">Stock Out</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>ics/stock_transfer.php">
|
||||
<a class="nav-link <?php echo wms_active('stock_transfer'); ?>" href="<?php echo $server_url?>ics/stock_transfer.php">
|
||||
<i class="ti ti-package"></i>
|
||||
<span class="nav-text">Stock Transfer</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>ics/sku_barcode_label.php">
|
||||
<a class="nav-link <?php echo wms_active('sku_barcode'); ?>" href="<?php echo $server_url?>ics/sku_barcode_label.php">
|
||||
<i class="ti ti-barcode"></i>
|
||||
<span class="nav-text">SKU Labels</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>ics/location_barcode_label.php">
|
||||
<a class="nav-link <?php echo wms_active('location_barcode'); ?>" href="<?php echo $server_url?>ics/location_barcode_label.php">
|
||||
<i class="ti ti-map-pin-code"></i>
|
||||
<span class="nav-text">Location Labels</span>
|
||||
</a>
|
||||
@@ -89,28 +95,28 @@
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>order/order.php">
|
||||
<a class="nav-link <?php echo wms_active('/order/order', '/order/manage_order', '/order/confirm_order'); ?>" href="<?php echo $server_url?>order/order.php">
|
||||
<i class="ti ti-shopping-cart"></i>
|
||||
<span class="nav-text">Sales Orders</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>order/return.php">
|
||||
<a class="nav-link <?php echo wms_active('/order/return', 'manage_return'); ?>" href="<?php echo $server_url?>order/return.php">
|
||||
<i class="ti ti-arrow-back-up"></i>
|
||||
<span class="nav-text">Returns</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>order/invoice.php">
|
||||
<a class="nav-link <?php echo wms_active('/order/invoice', 'manage_invoice', 'print_invoice'); ?>" href="<?php echo $server_url?>order/invoice.php">
|
||||
<i class="ti ti-file-invoice"></i>
|
||||
<span class="nav-text">Sales Invoices</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>po/po.php">
|
||||
<li>
|
||||
<a class="nav-link <?php echo wms_active('/po/'); ?>" href="<?php echo $server_url?>po/po.php">
|
||||
<i class="ti ti-clipboard-list"></i>
|
||||
<span class="nav-text">Purchase Orders</span>
|
||||
</a>
|
||||
@@ -122,28 +128,28 @@
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>reports/stock_movement.php">
|
||||
<a class="nav-link <?php echo wms_active('stock_movement'); ?>" href="<?php echo $server_url?>reports/stock_movement.php">
|
||||
<i class="ti ti-chart-bar"></i>
|
||||
<span class="nav-text">Stock Movement</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>reports/expired_stock.php">
|
||||
<a class="nav-link <?php echo wms_active('expired_stock'); ?>" href="<?php echo $server_url?>reports/expired_stock.php">
|
||||
<i class="ti ti-clock"></i>
|
||||
<span class="nav-text">Expired Stock</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>reports/occupy_rack.php">
|
||||
<a class="nav-link <?php echo wms_active('occupy_rack'); ?>" href="<?php echo $server_url?>reports/occupy_rack.php">
|
||||
<i class="ti ti-layout-grid"></i>
|
||||
<span class="nav-text">Rack Occupancy</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link" href="<?php echo $server_url?>reports/product_lot.php">
|
||||
<a class="nav-link <?php echo wms_active('product_lot'); ?>" href="<?php echo $server_url?>reports/product_lot.php">
|
||||
<i class="ti ti-packages"></i>
|
||||
<span class="nav-text">Product Lots</span>
|
||||
</a>
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
$sidebar_role = $_SESSION['login_role'] ?? 'viewer';
|
||||
$sidebar_can_staff = in_array($sidebar_role, ['owner', 'admin', 'staff'], true);
|
||||
$cp = $_SERVER['PHP_SELF'];
|
||||
function ac_active(...$frags) {
|
||||
global $cp;
|
||||
foreach ($frags as $f) { if (str_contains($cp, $f)) return 'active'; }
|
||||
return '';
|
||||
}
|
||||
?>
|
||||
<!-- SIDEBAR -->
|
||||
<aside id="sidebar" class="sidebar overflow-y-auto">
|
||||
<div class="logo-area">
|
||||
<a href="index.html" class="d-inline-flex">
|
||||
<img src="<?php echo $server_url?>assets/images/favicon.png" alt="" width="40"/>
|
||||
<span class="logo-text ms-2">
|
||||
<img src="<?php echo $server_url?>assets/images/logo.png" alt="" height="40" style="padding: 5px 0;"/>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<!-- Main -->
|
||||
<li class="nav-text-space">
|
||||
<small class="nav-text text-muted">Main</small>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('/ac_dashboard/'); ?>" href="<?php echo $server_url?>ac_dashboard/index.php">
|
||||
<i class="ti ti-home"></i>
|
||||
<span class="nav-text">Dashboard</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Revenue -->
|
||||
<li class="nav-text-space">
|
||||
<small class="nav-text text-muted">Revenue</small>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('/revenue/quotation'); ?>" href="<?php echo $server_url?>revenue/quotation.php">
|
||||
<i class="ti ti-file-description"></i>
|
||||
<span class="nav-text">Quotations</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('/revenue/order'); ?>" href="<?php echo $server_url?>revenue/order.php">
|
||||
<i class="ti ti-shopping-cart"></i>
|
||||
<span class="nav-text">Sales Orders</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('/revenue/invoice'); ?>" href="<?php echo $server_url?>revenue/invoice.php">
|
||||
<i class="ti ti-file-invoice"></i>
|
||||
<span class="nav-text">Sales Invoices</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('/revenue/receipt'); ?>" href="<?php echo $server_url?>revenue/receipt.php">
|
||||
<i class="ti ti-cash"></i>
|
||||
<span class="nav-text">Receipts</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Purchase -->
|
||||
<li class="nav-text-space">
|
||||
<small class="nav-text text-muted">Purchase</small>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('/expense/purchase_request'); ?>" href="<?php echo $server_url?>expense/purchase_request.php">
|
||||
<i class="ti ti-notes"></i>
|
||||
<span class="nav-text">Purchase Requests</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('/expense/purchase_order'); ?>" href="<?php echo $server_url?>expense/purchase_order.php">
|
||||
<i class="ti ti-clipboard-list"></i>
|
||||
<span class="nav-text">Purchase Orders</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('/expense/purchase_invoice'); ?>" href="<?php echo $server_url?>expense/purchase_invoice.php">
|
||||
<i class="ti ti-file-invoice"></i>
|
||||
<span class="nav-text">Purchase Invoices</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('/expense/payment'); ?>" href="<?php echo $server_url?>expense/payment.php">
|
||||
<i class="ti ti-credit-card"></i>
|
||||
<span class="nav-text">Payments</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Finance -->
|
||||
<li class="nav-text-space">
|
||||
<small class="nav-text text-muted">Finance</small>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('/finance/receipt'); ?>" href="<?php echo $server_url?>finance/receipt.php">
|
||||
<i class="ti ti-cash"></i>
|
||||
<span class="nav-text">Receipts</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('/finance/payment'); ?>" href="<?php echo $server_url?>finance/payment.php">
|
||||
<i class="ti ti-credit-card"></i>
|
||||
<span class="nav-text">Payments</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Journal Entries -->
|
||||
<li class="nav-text-space">
|
||||
<small class="nav-text text-muted">Journal Entries</small>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('/journal/index'); ?>" href="<?php echo $server_url?>journal/index.php">
|
||||
<i class="ti ti-books"></i>
|
||||
<span class="nav-text">Journal List</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('/journal/new'); ?>" href="<?php echo $server_url?>journal/new.php">
|
||||
<i class="ti ti-plus"></i>
|
||||
<span class="nav-text">New Entry</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Reports -->
|
||||
<li class="nav-text-space">
|
||||
<small class="nav-text text-muted">Reports</small>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('trial_balance'); ?>" href="<?php echo $server_url?>accounting/trial_balance.php">
|
||||
<i class="ti ti-scale"></i>
|
||||
<span class="nav-text">Trial Balance</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('pl_statement'); ?>" href="<?php echo $server_url?>accounting/pl_statement.php">
|
||||
<i class="ti ti-chart-line"></i>
|
||||
<span class="nav-text">P&L Statement</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('balance_sheet'); ?>" href="<?php echo $server_url?>accounting/balance_sheet.php">
|
||||
<i class="ti ti-table"></i>
|
||||
<span class="nav-text">Balance Sheet</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('gl_movement'); ?>" href="<?php echo $server_url?>accounting/gl_movement.php">
|
||||
<i class="ti ti-arrows-exchange"></i>
|
||||
<span class="nav-text">GL Movement</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="nav-link <?php echo ac_active('vat_report'); ?>" href="<?php echo $server_url?>accounting/vat_report.php">
|
||||
<i class="ti ti-receipt-tax"></i>
|
||||
<span class="nav-text">VAT Report</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li><br><br><br><br></li>
|
||||
|
||||
</ul>
|
||||
</aside>
|
||||
@@ -17,6 +17,36 @@ $topbar_picture = $_SESSION['login_profile_picture'] ?? '';
|
||||
$topbar_avatar_src = $topbar_picture
|
||||
? htmlspecialchars($server_url . 'uploads/profile/' . $topbar_picture, ENT_QUOTES, 'UTF-8')
|
||||
: htmlspecialchars($server_url . 'assets/images/avatar-1.jpg', ENT_QUOTES, 'UTF-8');
|
||||
|
||||
// ── App switcher ──────────────────────────────────────────────────────────────
|
||||
$topbar_app_access = $_SESSION['login_app_access'] ?? 'wms';
|
||||
|
||||
// Detect which app this page belongs to.
|
||||
// Master-data pages (/accounting/, /inventory/, /contact/, /setting/) are neutral — don't change the session.
|
||||
$_wms_paths = ['/dashboard/', '/ics/', '/order/', '/po/', '/reports/'];
|
||||
$_ac_paths = ['/ac_dashboard/', '/revenue/', '/expense/', '/finance/', '/journal/'];
|
||||
|
||||
$topbar_current_app = null; // null = neutral page (master data / setting)
|
||||
foreach ($_wms_paths as $_p) {
|
||||
if (str_contains($_SERVER['PHP_SELF'], $_p)) { $topbar_current_app = 'wms'; break; }
|
||||
}
|
||||
if (!$topbar_current_app) {
|
||||
foreach ($_ac_paths as $_p) {
|
||||
if (str_contains($_SERVER['PHP_SELF'], $_p)) { $topbar_current_app = 'accounting'; break; }
|
||||
}
|
||||
}
|
||||
|
||||
// Only persist to session when on a definitive app page
|
||||
if ($topbar_current_app) {
|
||||
$_SESSION['login_current_app'] = $topbar_current_app;
|
||||
}
|
||||
// Default session on first visit or neutral pages
|
||||
if (empty($_SESSION['login_current_app'])) {
|
||||
$_SESSION['login_current_app'] = ($topbar_app_access === 'accounting') ? 'accounting' : 'wms';
|
||||
}
|
||||
|
||||
$topbar_show_wms = in_array($topbar_app_access, ['all', 'wms']);
|
||||
$topbar_show_accounting = in_array($topbar_app_access, ['all', 'accounting']);
|
||||
?>
|
||||
|
||||
<div id="overlay" class="overlay"></div>
|
||||
@@ -59,6 +89,35 @@ $topbar_avatar_src = $topbar_picture
|
||||
<!-- Navbar nav -->
|
||||
<ul class="list-unstyled d-flex align-items-center mb-0 gap-1">
|
||||
|
||||
<!-- App switcher -->
|
||||
<?php if ($topbar_show_wms && $topbar_show_accounting): ?>
|
||||
<li class="dropdown">
|
||||
<a class="position-relative btn-icon btn-sm btn-light btn rounded-circle" data-bs-toggle="dropdown"
|
||||
aria-expanded="false" href="#" role="button" title="Switch App">
|
||||
<i class="ti ti-layout-grid" style="font-size:18px;"></i>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-end p-2" style="min-width:170px;">
|
||||
<?php $_sess_app = $_SESSION['login_current_app'] ?? 'wms'; ?>
|
||||
<a href="<?php echo $server_url?>dashboard/index.php"
|
||||
class="dropdown-item rounded d-flex align-items-center gap-2 py-2 <?php echo $_sess_app === 'wms' ? 'active' : ''; ?>">
|
||||
<i class="ti ti-box"></i>
|
||||
<span>WMS</span>
|
||||
<?php if ($_sess_app === 'wms'): ?>
|
||||
<i class="ti ti-check ms-auto text-success"></i>
|
||||
<?php endif; ?>
|
||||
</a>
|
||||
<a href="<?php echo $server_url?>ac_dashboard/index.php"
|
||||
class="dropdown-item rounded d-flex align-items-center gap-2 py-2 <?php echo $_sess_app === 'accounting' ? 'active' : ''; ?>">
|
||||
<i class="ti ti-calculator"></i>
|
||||
<span>Accounting</span>
|
||||
<?php if ($_sess_app === 'accounting'): ?>
|
||||
<i class="ti ti-check ms-auto text-success"></i>
|
||||
<?php endif; ?>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Bell icon -->
|
||||
<li>
|
||||
<a class="position-relative btn-icon btn-sm btn-light btn rounded-circle" data-bs-toggle="dropdown"
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">Journal List</h1>
|
||||
<p class="mb-0 text-muted">All journal entries</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-books fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">Journal List coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">New Journal Entry</h1>
|
||||
<p class="mb-0 text-muted">Create a manual journal entry</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-plus fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">New Journal Entry coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -124,6 +124,7 @@ $_SESSION["login_name"] = $temp["name"];
|
||||
$_SESSION["login_surname"] = $temp["surname"];
|
||||
$_SESSION["login_company_id"] = $temp["default_company"];
|
||||
$_SESSION["login_profile_picture"] = $temp["profile_picture"] ?? '';
|
||||
$_SESSION["login_app_access"] = $temp["app_access"] ?? 'wms';
|
||||
|
||||
$role_sth = $pdo1->prepare(
|
||||
"SELECT role FROM company_map_user
|
||||
|
||||
+5
-2
@@ -2,9 +2,12 @@
|
||||
require '../session.php';
|
||||
require '../config.php';
|
||||
require '../include_header.php';
|
||||
// successful login
|
||||
// successful login — redirect based on app_access
|
||||
if(!empty($_SESSION["login_status"])){
|
||||
header('Location: '.$server_url.'dashboard/index.php');
|
||||
$redirect = ($_SESSION['login_app_access'] ?? 'wms') === 'accounting'
|
||||
? $server_url . 'ac_dashboard/index.php'
|
||||
: $server_url . 'dashboard/index.php';
|
||||
header('Location: ' . $redirect);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,20 @@
|
||||
$data['items'] = json_decode($data['items'] ?? '[]', true) ?: [];
|
||||
|
||||
try {
|
||||
$new_id = null;
|
||||
$new_id = null;
|
||||
$quotation_id = (int)($data['quotation_id'] ?? 0);
|
||||
|
||||
dbTransaction($pdo2, function($pdo) use ($data, $company_id, $logging, &$new_id) {
|
||||
dbTransaction($pdo2, function($pdo) use ($data, $company_id, $logging, $quotation_id, &$new_id) {
|
||||
$order = new OrderManager($pdo, $company_id);
|
||||
$new_id = $order->saveOrder($data, $logging);
|
||||
|
||||
// On new order created from a quotation: link both records
|
||||
if ($new_id && $quotation_id > 0) {
|
||||
$pdo->prepare(
|
||||
"UPDATE ac_quotation SET order_id = :order_id, status = 5
|
||||
WHERE id = :id AND company_id = :cid AND status = 2"
|
||||
)->execute([':order_id' => $new_id, ':id' => $quotation_id, ':cid' => $company_id]);
|
||||
}
|
||||
});
|
||||
|
||||
$answer['success'] = 1;
|
||||
|
||||
@@ -244,6 +244,7 @@
|
||||
// ── Status helpers ────────────────────────────────────────────────────────
|
||||
function order_status_badge(status) {
|
||||
const map = {
|
||||
'-2': '<span class="badge bg-danger">Pending — Awaiting Warehouse</span>',
|
||||
'-1': '<span class="badge bg-secondary">Cancelled</span>',
|
||||
'0': '<span class="badge bg-warning text-dark">Draft</span>',
|
||||
'1': '<span class="badge bg-info text-white">Confirmed</span>',
|
||||
@@ -380,19 +381,26 @@
|
||||
has_active_invoice = has_active_invoice || false;
|
||||
returnable_count = returnable_count ?? 1;
|
||||
fulfillment_status = parseInt(fulfillment_status) || 0;
|
||||
var is_pending = status === -2;
|
||||
var is_draft = status === 0;
|
||||
var can_confirm = status === 0;
|
||||
var can_cancel = status === 0 || status === 1;
|
||||
var is_editable = is_pending || is_draft; // both Pending and Draft are editable
|
||||
var can_confirm = status === 0; // only Draft can be confirmed (not Pending)
|
||||
var can_cancel = status === -2 || status === 0 || status === 1;
|
||||
var is_cancelled = status === -1;
|
||||
var is_completed = status === 3;
|
||||
var can_fulfill = status >= 1 && !is_cancelled && !is_completed;
|
||||
|
||||
// Edit controls
|
||||
$('#btn_add_item, #contact, #order_date, #notes, #discount, #tax, #shipping_fee')
|
||||
.prop('disabled', !is_draft);
|
||||
$('#shipping_tracking_number').prop('disabled', !(is_draft || can_fulfill));
|
||||
$('.item_sku, .item_qty, .item_price, .item_warehouse').prop('disabled', !is_draft);
|
||||
$('a[onclick^="remove_item"]').toggleClass('d-none', !is_draft);
|
||||
.prop('disabled', !is_editable);
|
||||
$('#shipping_tracking_number').prop('disabled', !(is_editable || can_fulfill));
|
||||
$('.item_sku, .item_qty, .item_price, .item_warehouse').prop('disabled', !is_editable);
|
||||
$('a[onclick^="remove_item"]').toggleClass('d-none', !is_editable);
|
||||
|
||||
// Pending hint
|
||||
if (is_pending) {
|
||||
$('#page_subtitle').text('Assign a warehouse for each item, then save to move this order to Draft.');
|
||||
}
|
||||
|
||||
// Action buttons
|
||||
set_btn_state('#btn_save', is_draft, 'Only draft orders can be edited');
|
||||
|
||||
+3
-1
@@ -104,6 +104,7 @@
|
||||
<div class="col-lg-3">
|
||||
<select id="filter_status" class="form-select">
|
||||
<option value="">All Status</option>
|
||||
<option value="-2">Pending</option>
|
||||
<option value="-1">Cancelled</option>
|
||||
<option value="0">Draft</option>
|
||||
<option value="1">Confirmed</option>
|
||||
@@ -169,13 +170,14 @@
|
||||
function order_status_badge(status) {
|
||||
status = parseInt(status);
|
||||
const map = {
|
||||
'-2': '<span class="badge bg-danger">Pending</span>',
|
||||
'-1': '<span class="badge bg-secondary">Cancelled</span>',
|
||||
'0': '<span class="badge bg-warning text-dark">Draft</span>',
|
||||
'1': '<span class="badge bg-info text-white">Confirmed</span>',
|
||||
'2': '<span class="badge bg-primary">Processing</span>',
|
||||
'3': '<span class="badge bg-success">Completed</span>',
|
||||
};
|
||||
return map[status] ?? '<span class="badge bg-light text-dark">—</span>';
|
||||
return map[String(status)] ?? '<span class="badge bg-light text-dark">—</span>';
|
||||
}
|
||||
|
||||
function payment_status_badge(status) {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes/OrderManager.php';
|
||||
|
||||
$quotation_id = (int)($data['quotation_id'] ?? 0);
|
||||
if (!$quotation_id) {
|
||||
exit(json_encode(['success' => 0, 'message' => 'Quotation ID required']));
|
||||
}
|
||||
|
||||
// Load quotation — must be Accepted (status=2)
|
||||
$sth = $pdo2->prepare(
|
||||
"SELECT q.*, c.contact_name
|
||||
FROM ac_quotation q
|
||||
LEFT JOIN md_contact c ON c.id = q.contact_id
|
||||
WHERE q.id = :id AND q.company_id = :cid AND q.status = 2
|
||||
LIMIT 1"
|
||||
);
|
||||
$sth->execute([':id' => $quotation_id, ':cid' => $company_id]);
|
||||
$q = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$q) {
|
||||
exit(json_encode(['success' => 0, 'message' => 'Quotation not found or not in Accepted status']));
|
||||
}
|
||||
|
||||
// Check not already converted
|
||||
if ((int)$q['order_id'] > 0) {
|
||||
exit(json_encode(['success' => 0, 'message' => 'Quotation has already been converted to an order']));
|
||||
}
|
||||
|
||||
// Map quotation items → order items (warehouse_id left 0 = to be filled by WMS)
|
||||
$qt_items = json_decode($q['items'] ?? '[]', true) ?: [];
|
||||
$ord_items = [];
|
||||
foreach ($qt_items as $i => $qi) {
|
||||
$ord_items[] = [
|
||||
'item_id' => $i,
|
||||
'product_sku' => $qi['product_sku'] ?? '',
|
||||
'product_name' => $qi['description'] ?? '',
|
||||
'warehouse_id' => 0,
|
||||
'zone' => '',
|
||||
'aisle' => '',
|
||||
'rack' => '',
|
||||
'lot_number' => '',
|
||||
'serial_number' => '',
|
||||
'quantity' => (float)($qi['quantity'] ?? 0),
|
||||
'price' => (float)($qi['unit_price'] ?? 0),
|
||||
'total_price' => (float)($qi['total_price'] ?? 0),
|
||||
'stock_out_id' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$order_data = [
|
||||
'quotation_id' => $quotation_id,
|
||||
'contact_id' => (int)$q['contact_id'],
|
||||
'order_date' => date('Y-m-d'),
|
||||
'items' => $ord_items,
|
||||
'discount' => (float)$q['discount'],
|
||||
'tax' => (float)$q['tax'],
|
||||
'shipping_fee' => 0,
|
||||
'shipping_tracking_number' => '',
|
||||
'notes' => $q['notes'] ?? '',
|
||||
];
|
||||
|
||||
try {
|
||||
$new_order_id = null;
|
||||
|
||||
dbTransaction($pdo2, function($pdo) use ($order_data, $company_id, $logging, $quotation_id, &$new_order_id) {
|
||||
$order = new OrderManager($pdo, $company_id);
|
||||
$new_order_id = $order->saveOrder($order_data, $logging);
|
||||
|
||||
// Mark quotation as Converted and link order_id
|
||||
$pdo->prepare(
|
||||
"UPDATE ac_quotation SET status = 5, order_id = :oid
|
||||
WHERE id = :id AND company_id = :cid"
|
||||
)->execute([':oid' => $new_order_id, ':id' => $quotation_id, ':cid' => $company_id]);
|
||||
});
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Order created successfully';
|
||||
$answer['order_id'] = $new_order_id;
|
||||
|
||||
} catch (Exception $e) {
|
||||
$answer['message'] = $e->getMessage();
|
||||
http_response_code(400);
|
||||
}
|
||||
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
|
||||
$action = $data['action'] ?? '';
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
|
||||
// ── Generate quotation number ─────────────────────────────────────────────────
|
||||
function generate_quotation_number(PDO $pdo, int $cid): string {
|
||||
$prefix = 'QT-' . date('Ym') . '-';
|
||||
$sql = "SELECT quotation_number FROM ac_quotation
|
||||
WHERE company_id = :cid AND quotation_number LIKE :prefix
|
||||
ORDER BY id DESC LIMIT 1";
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute([':cid' => $cid, ':prefix' => $prefix . '%']);
|
||||
$last = $sth->fetchColumn();
|
||||
$seq = $last ? ((int)substr($last, -4) + 1) : 1;
|
||||
return $prefix . str_pad($seq, 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
// ── Compute totals from items ─────────────────────────────────────────────────
|
||||
function compute_totals(array $items, float $discount, float $tax): array {
|
||||
$subtotal = array_sum(array_map(fn($i) => (float)($i['total_price'] ?? 0), $items));
|
||||
$grand = $subtotal - $discount + $tax;
|
||||
return [round($subtotal, 4), round(max(0, $grand), 4)];
|
||||
}
|
||||
|
||||
if ($action === 'create') {
|
||||
|
||||
$items = json_decode($data['items'] ?? '[]', true) ?: [];
|
||||
$discount = (float)($data['discount'] ?? 0);
|
||||
$tax = (float)($data['tax'] ?? 0);
|
||||
[$subtotal, $grand] = compute_totals($items, $discount, $tax);
|
||||
|
||||
$qnum = generate_quotation_number($pdo2, $company_id);
|
||||
|
||||
$sql = "INSERT INTO ac_quotation
|
||||
(company_id, uuid, quotation_number, contact_id, quotation_date, valid_until,
|
||||
status, subtotal, discount, tax, grand_total, items, notes, log, created_at)
|
||||
VALUES
|
||||
(:cid, :uuid, :qnum, :contact_id, :qdate, :valid_until,
|
||||
0, :sub, :disc, :tax, :grand, :items, :notes, :log, NOW())";
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute([
|
||||
':cid' => $company_id,
|
||||
':uuid' => $uuid,
|
||||
':qnum' => $qnum,
|
||||
':contact_id' => (int)($data['contact_id'] ?? 0),
|
||||
':qdate' => $data['quotation_date'] ?: null,
|
||||
':valid_until'=> $data['valid_until'] ?: null,
|
||||
':sub' => $subtotal,
|
||||
':disc' => $discount,
|
||||
':tax' => $tax,
|
||||
':grand' => $grand,
|
||||
':items' => json_encode($items),
|
||||
':notes' => $data['notes'] ?? '',
|
||||
':log' => json_encode($logging),
|
||||
]);
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Quotation created';
|
||||
$answer['new_id'] = (int)$pdo2->lastInsertId();
|
||||
$answer['quotation_number'] = $qnum;
|
||||
|
||||
} elseif ($action === 'update') {
|
||||
|
||||
if (!$id) { exit(json_encode(['success' => 0, 'message' => 'ID required'])); }
|
||||
|
||||
$items = json_decode($data['items'] ?? '[]', true) ?: [];
|
||||
$discount = (float)($data['discount'] ?? 0);
|
||||
$tax = (float)($data['tax'] ?? 0);
|
||||
[$subtotal, $grand] = compute_totals($items, $discount, $tax);
|
||||
|
||||
$sql = "UPDATE ac_quotation SET
|
||||
contact_id = :contact_id,
|
||||
quotation_date = :qdate,
|
||||
valid_until = :valid_until,
|
||||
subtotal = :sub,
|
||||
discount = :disc,
|
||||
tax = :tax,
|
||||
grand_total = :grand,
|
||||
items = :items,
|
||||
notes = :notes
|
||||
WHERE id = :id AND company_id = :cid AND status = 0";
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute([
|
||||
':contact_id' => (int)($data['contact_id'] ?? 0),
|
||||
':qdate' => $data['quotation_date'] ?: null,
|
||||
':valid_until' => $data['valid_until'] ?: null,
|
||||
':sub' => $subtotal,
|
||||
':disc' => $discount,
|
||||
':tax' => $tax,
|
||||
':grand' => $grand,
|
||||
':items' => json_encode($items),
|
||||
':notes' => $data['notes'] ?? '',
|
||||
':id' => $id,
|
||||
':cid' => $company_id,
|
||||
]);
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Quotation updated';
|
||||
|
||||
} else {
|
||||
$answer['message'] = 'Unknown action';
|
||||
}
|
||||
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
|
||||
$sql = "SELECT
|
||||
COUNT(*) AS total,
|
||||
SUM(status = 0) AS draft,
|
||||
SUM(status = 1) AS sent,
|
||||
SUM(status = 2) AS accepted,
|
||||
SUM(status = 3) AS rejected
|
||||
FROM ac_quotation
|
||||
WHERE company_id = :cid";
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute([':cid' => $company_id]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['output'] = $row;
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
|
||||
if ($id) {
|
||||
// Single quotation
|
||||
$sql = "SELECT q.*, c.contact_name
|
||||
FROM ac_quotation q
|
||||
LEFT JOIN md_contact c ON c.id = q.contact_id
|
||||
WHERE q.id = :id AND q.company_id = :cid
|
||||
LIMIT 1";
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute([':id' => $id, ':cid' => $company_id]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
if (!$row) { exit(json_encode(['success' => 0, 'message' => 'Not found'])); }
|
||||
$row['items'] = json_decode($row['items'] ?? '[]', true);
|
||||
$answer['output'] = $row;
|
||||
} else {
|
||||
// List
|
||||
$sql = "SELECT q.*, c.contact_name
|
||||
FROM ac_quotation q
|
||||
LEFT JOIN md_contact c ON c.id = q.contact_id
|
||||
WHERE q.company_id = :cid
|
||||
ORDER BY q.id DESC";
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute([':cid' => $company_id]);
|
||||
$answer['output'] = $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
$answer['success'] = 1;
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
$action = $data['action_type'] ?? '';
|
||||
|
||||
if (!$id || !$action) {
|
||||
exit(json_encode(['success' => 0, 'message' => 'ID and action required']));
|
||||
}
|
||||
|
||||
// Load current status
|
||||
$sth = $pdo2->prepare("SELECT status FROM ac_quotation WHERE id = :id AND company_id = :cid LIMIT 1");
|
||||
$sth->execute([':id' => $id, ':cid' => $company_id]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
if (!$row) { exit(json_encode(['success' => 0, 'message' => 'Not found'])); }
|
||||
|
||||
$cur = (int)$row['status'];
|
||||
|
||||
$transitions = [
|
||||
'send' => ['from' => [0], 'to' => 1, 'label' => 'Sent'],
|
||||
'accept' => ['from' => [1], 'to' => 2, 'label' => 'Accepted'],
|
||||
'reject' => ['from' => [1], 'to' => 3, 'label' => 'Rejected'],
|
||||
'reopen' => ['from' => [1,2,3],'to' => 0, 'label' => 'Draft'],
|
||||
'cancel' => ['from' => [0,1], 'to' => -1,'label' => 'Cancelled'],
|
||||
];
|
||||
|
||||
if (!isset($transitions[$action])) {
|
||||
exit(json_encode(['success' => 0, 'message' => 'Invalid action']));
|
||||
}
|
||||
|
||||
$t = $transitions[$action];
|
||||
if (!in_array($cur, $t['from'], true)) {
|
||||
exit(json_encode(['success' => 0, 'message' => 'Transition not allowed from current status']));
|
||||
}
|
||||
|
||||
$sth = $pdo2->prepare("UPDATE ac_quotation SET status = :status WHERE id = :id AND company_id = :cid");
|
||||
$sth->execute([':status' => $t['to'], ':id' => $id, ':cid' => $company_id]);
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Status updated to ' . $t['label'];
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">Sales Invoices</h1>
|
||||
<p class="mb-0 text-muted">Sales invoice management</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-file-invoice fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">Sales Invoices coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,500 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../config.php';
|
||||
require '../include_header.php';
|
||||
|
||||
$quotation_id = (int)($_GET['id'] ?? 0);
|
||||
?>
|
||||
|
||||
<body>
|
||||
<?php require '../include_topbar.php'; ?>
|
||||
<?php require '../include_sidebar_ac.php'; ?>
|
||||
|
||||
<main id="content" class="content py-15">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Page header -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="mb-6 d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h1 class="fs-3 mb-1" id="page_title">
|
||||
<?php echo $quotation_id ? 'Quotation Detail' : 'New Quotation'; ?>
|
||||
</h1>
|
||||
<p class="mb-0 text-muted" id="page_subtitle">
|
||||
<?php echo $quotation_id ? '' : 'Create a new sales quotation'; ?>
|
||||
</p>
|
||||
</div>
|
||||
<a href="<?php echo $server_url?>revenue/quotation.php" class="btn btn-light">
|
||||
<i class="ti ti-arrow-left me-1"></i>Back
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-5">
|
||||
|
||||
<!-- Left: Quotation form -->
|
||||
<div class="col-lg-8">
|
||||
|
||||
<!-- Header card -->
|
||||
<div class="card mb-5">
|
||||
<div class="card-body p-5">
|
||||
<h2 class="fs-5 mb-4">Quotation Information</h2>
|
||||
<div class="row gx-5">
|
||||
|
||||
<div class="mb-3 col-lg-6">
|
||||
<label class="form-label">Contact</label>
|
||||
<input type="text" id="contact" class="form-control"
|
||||
placeholder="Search contact..." autocomplete="off">
|
||||
<input type="hidden" id="contact_id">
|
||||
</div>
|
||||
|
||||
<div class="mb-3 col-lg-6">
|
||||
<label class="form-label">Quotation Date</label>
|
||||
<input type="text" id="quotation_date" class="form-control"
|
||||
placeholder="DD/MM/YYYY" autocomplete="off">
|
||||
</div>
|
||||
|
||||
<div class="mb-3 col-lg-6">
|
||||
<label class="form-label">Valid Until</label>
|
||||
<input type="text" id="valid_until" class="form-control"
|
||||
placeholder="DD/MM/YYYY (optional)" autocomplete="off">
|
||||
</div>
|
||||
|
||||
<div class="mb-3 col-lg-12">
|
||||
<label class="form-label">Notes / Terms</label>
|
||||
<textarea id="notes" class="form-control" rows="2"
|
||||
placeholder="Payment terms, delivery conditions, notes..."></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Items card -->
|
||||
<div class="card mb-5">
|
||||
<div class="card-body p-5">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="fs-5 mb-0">Line Items</h2>
|
||||
<button class="btn btn-sm btn-outline-primary" id="btn_add_item"
|
||||
onclick="add_item_row()">
|
||||
<i class="ti ti-plus me-1"></i>Add Line
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table mb-0" id="item_table">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th style="width:120px;">SKU</th>
|
||||
<th>Description</th>
|
||||
<th style="width:90px;">Qty</th>
|
||||
<th style="width:120px;">Unit Price</th>
|
||||
<th style="width:120px;">Total</th>
|
||||
<th style="width:40px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="item_tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Right: Status + Summary + Actions -->
|
||||
<div class="col-lg-4">
|
||||
|
||||
<!-- Status card (existing quotation) -->
|
||||
<div class="card mb-5 d-none" id="status_card">
|
||||
<div class="card-body p-4">
|
||||
<h2 class="fs-5 mb-3">Status</h2>
|
||||
<div id="badge_status" class="mb-2"></div>
|
||||
<p class="mb-0 small fw-semibold text-muted" id="quotation_number_display"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Totals -->
|
||||
<div class="card mb-5">
|
||||
<div class="card-body p-4">
|
||||
<h2 class="fs-5 mb-3">Summary</h2>
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<span class="text-muted">Subtotal</span>
|
||||
<span id="summary_subtotal">0.00</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="text-muted">Discount</span>
|
||||
<input type="number" id="discount" class="form-control form-control-sm text-end"
|
||||
style="width:110px;" value="0" min="0" step="any" oninput="recalc_totals()">
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<span class="text-muted">Tax (VAT)</span>
|
||||
<input type="number" id="tax" class="form-control form-control-sm text-end"
|
||||
style="width:110px;" value="0" min="0" step="any" oninput="recalc_totals()">
|
||||
</div>
|
||||
<hr>
|
||||
<div class="d-flex justify-content-between fw-bold fs-5">
|
||||
<span>Grand Total</span>
|
||||
<span id="summary_grand_total">0.00</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="card">
|
||||
<div class="card-body p-4 d-flex flex-column gap-2">
|
||||
|
||||
<button class="btn btn-primary w-100" id="btn_save" onclick="save_quotation()">
|
||||
<i class="ti ti-device-floppy me-1"></i>Save Draft
|
||||
</button>
|
||||
|
||||
<button class="btn btn-info text-white w-100 d-none" id="btn_send"
|
||||
onclick="change_status('send')">
|
||||
<i class="ti ti-send me-1"></i>Mark as Sent
|
||||
</button>
|
||||
|
||||
<button class="btn btn-success w-100 d-none" id="btn_accept"
|
||||
onclick="change_status('accept')">
|
||||
<i class="ti ti-circle-check me-1"></i>Mark as Accepted
|
||||
</button>
|
||||
|
||||
<button class="btn btn-outline-danger w-100 d-none" id="btn_reject"
|
||||
onclick="change_status('reject')">
|
||||
<i class="ti ti-x me-1"></i>Mark as Rejected
|
||||
</button>
|
||||
|
||||
<button class="btn btn-outline-secondary w-100 d-none" id="btn_reopen"
|
||||
onclick="change_status('reopen')">
|
||||
<i class="ti ti-refresh me-1"></i>Reopen as Draft
|
||||
</button>
|
||||
|
||||
<button class="btn btn-outline-danger w-100 d-none" id="btn_cancel"
|
||||
onclick="change_status('cancel')">
|
||||
<i class="ti ti-ban me-1"></i>Cancel Quotation
|
||||
</button>
|
||||
|
||||
<hr class="d-none" id="hr_convert">
|
||||
<a href="javascript:void(0);" class="btn btn-success w-100 d-none" id="btn_convert"
|
||||
onclick="convert_to_order()">
|
||||
<i class="ti ti-transfer me-1"></i>Convert to Order
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
<script>
|
||||
|
||||
var quotation_id = <?php echo $quotation_id; ?>;
|
||||
var quotation_data = null;
|
||||
var item_index = 0;
|
||||
|
||||
var status_badge = {
|
||||
'-1': '<span class="badge bg-secondary">Cancelled</span>',
|
||||
'0': '<span class="badge bg-warning text-dark">Draft</span>',
|
||||
'1': '<span class="badge bg-info text-white">Sent</span>',
|
||||
'2': '<span class="badge bg-success">Accepted</span>',
|
||||
'3': '<span class="badge bg-danger">Rejected</span>',
|
||||
'5': '<span class="badge bg-primary">Converted</span>',
|
||||
};
|
||||
|
||||
|
||||
// ── Recalculate totals ────────────────────────────────────────────────────
|
||||
function recalc_totals() {
|
||||
var subtotal = 0;
|
||||
$('#item_tbody tr').each(function() {
|
||||
var qty = parseFloat($(this).find('.item_qty').val()) || 0;
|
||||
var price = parseFloat($(this).find('.item_price').val()) || 0;
|
||||
var total = qty * price;
|
||||
$(this).find('.item_total').text(format_number(total, 2));
|
||||
subtotal += total;
|
||||
});
|
||||
|
||||
var discount = parseFloat($('#discount').val()) || 0;
|
||||
var tax = parseFloat($('#tax').val()) || 0;
|
||||
var grand = subtotal - discount + tax;
|
||||
|
||||
$('#summary_subtotal').text(format_number(subtotal, 2));
|
||||
$('#summary_grand_total').text(format_number(grand, 2));
|
||||
}
|
||||
|
||||
|
||||
// ── Add item row ──────────────────────────────────────────────────────────
|
||||
function add_item_row(item) {
|
||||
item = item || {};
|
||||
var idx = item_index++;
|
||||
var row = `<tr id="item_row_${idx}">
|
||||
<td>
|
||||
<input type="text" class="form-control form-control-sm item_sku"
|
||||
placeholder="SKU..." value="${escape_html(item.product_sku || '')}" autocomplete="off">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" class="form-control form-control-sm item_desc"
|
||||
placeholder="Description..." value="${escape_html(item.description || '')}" autocomplete="off">
|
||||
</td>
|
||||
<td>
|
||||
<input type="number" class="form-control form-control-sm item_qty"
|
||||
value="${item.quantity || 1}" min="0.0001" step="any" oninput="recalc_totals()">
|
||||
</td>
|
||||
<td>
|
||||
<input type="number" class="form-control form-control-sm item_price"
|
||||
value="${item.unit_price || 0}" min="0" step="any" oninput="recalc_totals()">
|
||||
</td>
|
||||
<td class="item_total text-end align-middle">
|
||||
${format_number((item.quantity || 1) * (item.unit_price || 0), 2)}
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
<a href="javascript:void(0);" class="link-danger remove_item_btn" onclick="remove_item(${idx})">
|
||||
<i class="ti ti-trash fs-5"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>`;
|
||||
$('#item_tbody').append(row);
|
||||
recalc_totals();
|
||||
}
|
||||
|
||||
function remove_item(idx) {
|
||||
$(`#item_row_${idx}`).remove();
|
||||
recalc_totals();
|
||||
}
|
||||
|
||||
|
||||
// ── Collect items from DOM ────────────────────────────────────────────────
|
||||
function collect_items() {
|
||||
var items = [];
|
||||
$('#item_tbody tr').each(function() {
|
||||
var sku = $(this).find('.item_sku').val().trim();
|
||||
var desc = $(this).find('.item_desc').val().trim();
|
||||
var qty = parseFloat($(this).find('.item_qty').val()) || 0;
|
||||
var price = parseFloat($(this).find('.item_price').val()) || 0;
|
||||
if (!desc && !sku && qty === 0) return;
|
||||
items.push({
|
||||
product_sku: sku,
|
||||
description: desc,
|
||||
quantity: qty,
|
||||
unit_price: price,
|
||||
total_price: parseFloat((qty * price).toFixed(4)),
|
||||
});
|
||||
});
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
// ── Set UI mode based on status ───────────────────────────────────────────
|
||||
function set_mode(status) {
|
||||
status = parseInt(status);
|
||||
var is_draft = status === 0;
|
||||
var is_sent = status === 1;
|
||||
var is_accepted = status === 2;
|
||||
var is_converted = status === 5;
|
||||
var is_closed = status >= 2 || status === -1;
|
||||
var is_editable = is_draft;
|
||||
|
||||
// Edit controls
|
||||
$('#contact, #quotation_date, #valid_until, #notes, #discount, #tax, #btn_add_item')
|
||||
.prop('disabled', !is_editable);
|
||||
$('.item_sku, .item_desc, .item_qty, .item_price').prop('disabled', !is_editable);
|
||||
$('.remove_item_btn').toggleClass('d-none', !is_editable);
|
||||
|
||||
// Action buttons
|
||||
$('#btn_save').toggleClass('d-none', !is_draft);
|
||||
$('#btn_send').toggleClass('d-none', !is_draft);
|
||||
$('#btn_accept').toggleClass('d-none', !is_sent);
|
||||
$('#btn_reject').toggleClass('d-none', !is_sent);
|
||||
$('#btn_reopen').toggleClass('d-none', is_draft || is_closed);
|
||||
$('#btn_cancel').toggleClass('d-none', is_closed || (is_draft && !quotation_id));
|
||||
|
||||
// Convert to Order — only on Accepted, not yet converted
|
||||
var show_convert = is_accepted && !is_converted;
|
||||
$('#hr_convert').toggleClass('d-none', !show_convert);
|
||||
$('#btn_convert').toggleClass('d-none', !show_convert);
|
||||
}
|
||||
|
||||
|
||||
// ── Load existing quotation ───────────────────────────────────────────────
|
||||
function retrieve_quotation() {
|
||||
if (!quotation_id) return Promise.resolve();
|
||||
return ajax_request({
|
||||
url: '<?php echo $server_url?>revenue/api/engine/retrieve_quotation.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'read',
|
||||
data: { id: quotation_id },
|
||||
onSuccess: function(res) {
|
||||
quotation_data = res.output;
|
||||
var q = quotation_data;
|
||||
|
||||
$('#page_title').text(q.quotation_number);
|
||||
$('#page_subtitle').text('');
|
||||
$('#status_card').removeClass('d-none');
|
||||
$('#badge_status').html(status_badge[String(q.status)] || q.status);
|
||||
$('#quotation_number_display').text(q.quotation_number);
|
||||
|
||||
$('#contact').val(q.contact_name || '');
|
||||
$('#contact_id').val(q.contact_id || 0);
|
||||
$('#quotation_date').val(q.quotation_date ? format_date(q.quotation_date) : '');
|
||||
$('#valid_until').val(q.valid_until ? format_date(q.valid_until) : '');
|
||||
$('#notes').val(q.notes || '');
|
||||
$('#discount').val(parseFloat(q.discount) || 0);
|
||||
$('#tax').val(parseFloat(q.tax) || 0);
|
||||
|
||||
$('#item_tbody').empty();
|
||||
item_index = 0;
|
||||
(q.items || []).forEach(function(item) { add_item_row(item); });
|
||||
|
||||
recalc_totals();
|
||||
set_mode(q.status);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ── Save draft ────────────────────────────────────────────────────────────
|
||||
function save_quotation() {
|
||||
var items = collect_items();
|
||||
if (!items.length) {
|
||||
bootbox.alert('Please add at least one line item.');
|
||||
return;
|
||||
}
|
||||
|
||||
ajax_request({
|
||||
url: '<?php echo $server_url?>revenue/api/engine/manage_quotation.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: quotation_id ? 'update' : 'create',
|
||||
data: {
|
||||
id: quotation_id,
|
||||
contact_id: $('#contact_id').val() || 0,
|
||||
quotation_date: to_iso_date($('#quotation_date').val()),
|
||||
valid_until: to_iso_date($('#valid_until').val()),
|
||||
items: JSON.stringify(items),
|
||||
discount: $('#discount').val() || 0,
|
||||
tax: $('#tax').val() || 0,
|
||||
notes: $('#notes').val(),
|
||||
},
|
||||
onSuccess: function(res) {
|
||||
if (!quotation_id && res.new_id) {
|
||||
window.location.href = '<?php echo $server_url?>revenue/manage_quotation.php?id=' + res.new_id;
|
||||
} else {
|
||||
retrieve_quotation();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ── Change status ─────────────────────────────────────────────────────────
|
||||
function change_status(action_type) {
|
||||
var labels = {
|
||||
send: 'Mark this quotation as Sent to customer?',
|
||||
accept: 'Mark this quotation as Accepted?',
|
||||
reject: 'Mark this quotation as Rejected?',
|
||||
reopen: 'Reopen this quotation as Draft?',
|
||||
cancel: 'Cancel this quotation?',
|
||||
};
|
||||
bootbox.confirm({
|
||||
message: labels[action_type] || 'Confirm?',
|
||||
buttons: {
|
||||
confirm: { label: 'Confirm', className: 'btn-primary' },
|
||||
cancel: { label: 'Back', className: 'btn-secondary' }
|
||||
},
|
||||
callback: function(result) {
|
||||
if (!result) return;
|
||||
ajax_request({
|
||||
url: '<?php echo $server_url?>revenue/api/engine/update_quotation_status.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'update',
|
||||
data: { id: quotation_id, action_type: action_type },
|
||||
onSuccess: function() {
|
||||
retrieve_quotation();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ── Convert to Order ─────────────────────────────────────────────────────
|
||||
function convert_to_order() {
|
||||
bootbox.confirm({
|
||||
message: 'Convert this quotation to a Pending sales order? WMS staff will assign warehouse locations before confirming.',
|
||||
buttons: {
|
||||
confirm: { label: 'Convert to Order', className: 'btn-success' },
|
||||
cancel: { label: 'Back', className: 'btn-secondary' }
|
||||
},
|
||||
callback: function(result) {
|
||||
if (!result) return;
|
||||
ajax_request({
|
||||
url: '<?php echo $server_url?>revenue/api/engine/convert_quotation.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'create',
|
||||
data: { quotation_id: quotation_id },
|
||||
onSuccess: function(res) {
|
||||
retrieve_quotation(); // reload — status now shows Converted
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ── Contact autocomplete ──────────────────────────────────────────────────
|
||||
function init_contact_search() {
|
||||
$('#contact').autocomplete({
|
||||
source: function(req, res) {
|
||||
ajax_request({
|
||||
url: '<?php echo $server_url?>contact/api/engine/contact.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'read',
|
||||
data: { keyword: req.term },
|
||||
onSuccess: function(data) {
|
||||
res((data.output || []).map(c => ({
|
||||
label: c.contact_name,
|
||||
value: c.contact_name,
|
||||
id: c.id,
|
||||
})));
|
||||
}
|
||||
});
|
||||
},
|
||||
select: function(e, ui) {
|
||||
$('#contact_id').val(ui.item.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ── Boot ──────────────────────────────────────────────────────────────────
|
||||
$(async function() {
|
||||
try {
|
||||
if (quotation_id) {
|
||||
await retrieve_quotation();
|
||||
} else {
|
||||
var today = flatpickr.formatDate(new Date(), 'd/m/Y');
|
||||
$('#quotation_date').val(today);
|
||||
add_item_row();
|
||||
set_mode(0);
|
||||
}
|
||||
|
||||
flatpickr('#quotation_date', { dateFormat: 'd/m/Y', allowInput: true });
|
||||
flatpickr('#valid_until', { dateFormat: 'd/m/Y', allowInput: true });
|
||||
init_contact_search();
|
||||
|
||||
} catch(e) { console.log(e); }
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">Sales Orders</h1>
|
||||
<p class="mb-0 text-muted">Sales order management</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-shopping-cart fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">Sales Orders coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,235 @@
|
||||
<?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">
|
||||
|
||||
<!-- Page header -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="mb-6 d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h1 class="fs-3 mb-1">Quotations</h1>
|
||||
<p class="mb-0 text-muted">Sales quotations sent to customers</p>
|
||||
</div>
|
||||
<a href="<?php echo $server_url?>revenue/manage_quotation.php" class="btn btn-primary">
|
||||
<i class="ti ti-plus me-1"></i>New Quotation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stat cards -->
|
||||
<div class="row g-5 mb-5">
|
||||
<div class="col-lg-3 col-sm-6 col-12">
|
||||
<div class="card p-4">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="icon-shape icon-md bg-primary bg-opacity-10 text-primary rounded-2">
|
||||
<i class="ti ti-file-description fs-4"></i>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-0 text-muted small">Total</p>
|
||||
<h3 class="fw-bold mb-0" id="stat_total">—</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-sm-6 col-12">
|
||||
<div class="card p-4">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="icon-shape icon-md bg-warning bg-opacity-10 text-warning rounded-2">
|
||||
<i class="ti ti-pencil fs-4"></i>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-0 text-muted small">Draft</p>
|
||||
<h3 class="fw-bold mb-0" id="stat_draft">—</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-sm-6 col-12">
|
||||
<div class="card p-4">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="icon-shape icon-md bg-info bg-opacity-10 text-info rounded-2">
|
||||
<i class="ti ti-send fs-4"></i>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-0 text-muted small">Sent</p>
|
||||
<h3 class="fw-bold mb-0" id="stat_sent">—</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-sm-6 col-12">
|
||||
<div class="card p-4">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="icon-shape icon-md bg-success bg-opacity-10 text-success rounded-2">
|
||||
<i class="ti ti-circle-check fs-4"></i>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-0 text-muted small">Accepted</p>
|
||||
<h3 class="fw-bold mb-0" id="stat_accepted">—</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filter + Table -->
|
||||
<div class="row g-5">
|
||||
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body p-4">
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-lg-6">
|
||||
<div class="position-relative">
|
||||
<input type="text" id="search_input" class="form-control ps-9"
|
||||
placeholder="Search quotation number or contact...">
|
||||
<span class="position-absolute top-25 ms-4">
|
||||
<i class="ti ti-search text-muted"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<select id="filter_status" class="form-select">
|
||||
<option value="">All Status</option>
|
||||
<option value="0">Draft</option>
|
||||
<option value="1">Sent</option>
|
||||
<option value="2">Accepted</option>
|
||||
<option value="3">Rejected</option>
|
||||
<option value="5">Converted</option>
|
||||
<option value="-1">Cancelled</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<button class="btn btn-secondary w-100" onclick="retrieve_quotations()">
|
||||
<i class="ti ti-search me-1"></i>Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body p-5">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 table-centered" id="quotation_list">
|
||||
<thead class="table-primary border-light">
|
||||
<tr>
|
||||
<th>Quotation #</th>
|
||||
<th>Date</th>
|
||||
<th>Valid Until</th>
|
||||
<th>Contact</th>
|
||||
<th>Items</th>
|
||||
<th>Grand Total</th>
|
||||
<th>Status</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
<tfoot></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
<script>
|
||||
|
||||
var status_badge = {
|
||||
'-1': '<span class="badge bg-secondary">Cancelled</span>',
|
||||
'0': '<span class="badge bg-warning text-dark">Draft</span>',
|
||||
'1': '<span class="badge bg-info text-white">Sent</span>',
|
||||
'2': '<span class="badge bg-success">Accepted</span>',
|
||||
'3': '<span class="badge bg-danger">Rejected</span>',
|
||||
'5': '<span class="badge bg-primary">Converted</span>',
|
||||
};
|
||||
|
||||
function retrieve_quotations() {
|
||||
return ajax_request({
|
||||
url: '<?php echo $server_url?>revenue/api/engine/retrieve_quotation.php',
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'read',
|
||||
onSuccess: function(res) {
|
||||
var all = res.output || [];
|
||||
|
||||
var keyword = $('#search_input').val().toLowerCase();
|
||||
var fil_status = $('#filter_status').val();
|
||||
|
||||
var data = all.filter(function(q) {
|
||||
var match_kw = !keyword ||
|
||||
(q.quotation_number || '').toLowerCase().includes(keyword) ||
|
||||
(q.contact_name || '').toLowerCase().includes(keyword);
|
||||
var match_st = fil_status === '' || String(q.status) === fil_status;
|
||||
return match_kw && match_st;
|
||||
});
|
||||
|
||||
// Stats from full list (unfiltered)
|
||||
$('#stat_total').text(all.length);
|
||||
$('#stat_draft').text(all.filter(q => String(q.status) === '0').length);
|
||||
$('#stat_sent').text(all.filter(q => String(q.status) === '1').length);
|
||||
$('#stat_accepted').text(all.filter(q => String(q.status) === '2').length);
|
||||
|
||||
alasql('CREATE TABLE IF NOT EXISTS quotation_list');
|
||||
alasql.tables.quotation_list.data = data;
|
||||
var page = generate_pagination('quotation_list', data.length);
|
||||
$('table#quotation_list tfoot').html(page);
|
||||
if (typeof change_page_quotation_list === 'function') change_page_quotation_list(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function change_page_quotation_list(page_num) {
|
||||
var offset = (page_num - 1) * prop_limit;
|
||||
var page_data = alasql(`SELECT * FROM quotation_list LIMIT ${prop_limit} OFFSET ${offset}`);
|
||||
var body = '';
|
||||
|
||||
$.each(page_data, function(i, q) {
|
||||
var items = typeof q.items === 'string' ? JSON.parse(q.items || '[]') : (q.items || []);
|
||||
body += `<tr>
|
||||
<td class="py-3 fw-semibold">${escape_html(q.quotation_number)}</td>
|
||||
<td class="py-3">${format_date(q.quotation_date)}</td>
|
||||
<td class="py-3">${q.valid_until ? format_date(q.valid_until) : '<span class="text-muted">—</span>'}</td>
|
||||
<td class="py-3">${escape_html(q.contact_name || '—')}</td>
|
||||
<td class="py-3">${items.length} item${items.length !== 1 ? 's' : ''}</td>
|
||||
<td class="py-3 fw-semibold">${format_number(q.grand_total, 2)}</td>
|
||||
<td class="py-3">${status_badge[String(q.status)] || q.status}</td>
|
||||
<td class="py-3">
|
||||
<a href="<?php echo $server_url?>revenue/manage_quotation.php?id=${q.id}" title="View">
|
||||
<i class="ti ti-eye fs-5"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>`;
|
||||
});
|
||||
|
||||
$('table#quotation_list tbody').html(body);
|
||||
}
|
||||
|
||||
$(function() {
|
||||
retrieve_quotations();
|
||||
$('#search_input').on('keyup', function(e) {
|
||||
if (e.key === 'Enter') retrieve_quotations();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
<h1 class="fs-3 mb-1">Receipts</h1>
|
||||
<p class="mb-0 text-muted">Customer receipts</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card p-5 text-center text-muted">
|
||||
<i class="ti ti-cash fs-1 mb-3 d-block"></i>
|
||||
<p class="mb-0">Receipts coming soon.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php require '../include_ending.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,4 @@
|
||||
-- db: wms
|
||||
ALTER TABLE `user`
|
||||
ADD COLUMN `app_access` ENUM('all', 'wms', 'accounting') NOT NULL DEFAULT 'wms'
|
||||
AFTER `license`;
|
||||
@@ -0,0 +1,87 @@
|
||||
-- db: wms2
|
||||
|
||||
-- ── Chart of Accounts ────────────────────────────────────────────────────────
|
||||
CREATE TABLE `ac_account` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`company_id` int(11) NOT NULL,
|
||||
`account_code` varchar(20) NOT NULL,
|
||||
`account_name` varchar(255) NOT NULL,
|
||||
`account_type` ENUM('asset','liability','equity','revenue','expense') NOT NULL,
|
||||
`parent_code` varchar(20) DEFAULT NULL,
|
||||
`is_posting` tinyint(1) NOT NULL DEFAULT 1 COMMENT '0=header only, 1=can post',
|
||||
`status` tinyint(1) NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_company_account` (`company_id`, `account_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
|
||||
|
||||
-- ── Departments ───────────────────────────────────────────────────────────────
|
||||
CREATE TABLE `ac_department` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`company_id` int(11) NOT NULL,
|
||||
`dept_code` varchar(20) NOT NULL,
|
||||
`dept_name` varchar(255) NOT NULL,
|
||||
`description` text DEFAULT NULL,
|
||||
`status` tinyint(1) NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_company_dept` (`company_id`, `dept_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
|
||||
|
||||
-- ── Accounting Periods ────────────────────────────────────────────────────────
|
||||
CREATE TABLE `ac_period` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`company_id` int(11) NOT NULL,
|
||||
`period` varchar(7) NOT NULL COMMENT 'YYYY-MM',
|
||||
`status` ENUM('open','closed','locked') NOT NULL DEFAULT 'open',
|
||||
`closed_at` datetime DEFAULT NULL,
|
||||
`locked_at` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_company_period` (`company_id`, `period`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
|
||||
|
||||
-- ── Journal Entry Headers ─────────────────────────────────────────────────────
|
||||
CREATE TABLE `ac_journal` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`company_id` int(11) NOT NULL,
|
||||
`je_number` varchar(50) NOT NULL,
|
||||
`period` varchar(7) NOT NULL COMMENT 'YYYY-MM',
|
||||
`je_date` date NOT NULL,
|
||||
`description` text DEFAULT NULL,
|
||||
`source_type` varchar(50) DEFAULT NULL COMMENT 'invoice, po, receipt, payment, manual',
|
||||
`source_id` int(11) DEFAULT NULL,
|
||||
`status` tinyint(1) NOT NULL DEFAULT 1 COMMENT '1=posted, 0=draft, -1=voided',
|
||||
`created_by` int(11) DEFAULT NULL,
|
||||
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_company_je` (`company_id`, `je_number`),
|
||||
KEY `idx_period` (`company_id`, `period`),
|
||||
KEY `idx_source` (`company_id`, `source_type`, `source_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
|
||||
|
||||
-- ── Journal Entry Lines ───────────────────────────────────────────────────────
|
||||
CREATE TABLE `ac_journal_line` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`company_id` int(11) NOT NULL,
|
||||
`journal_id` int(11) NOT NULL,
|
||||
`account_code` varchar(20) NOT NULL,
|
||||
`dept_id` int(11) DEFAULT NULL,
|
||||
`description` text DEFAULT NULL,
|
||||
`debit` decimal(15,2) NOT NULL DEFAULT 0.00,
|
||||
`credit` decimal(15,2) NOT NULL DEFAULT 0.00,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_journal` (`journal_id`),
|
||||
KEY `idx_account` (`company_id`, `account_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
|
||||
|
||||
-- ── Period Balance Summary ────────────────────────────────────────────────────
|
||||
-- Mirrors warehouse_balance: aggregated debit/credit per account+dept+period
|
||||
CREATE TABLE `ac_balance` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`company_id` int(11) NOT NULL,
|
||||
`period` varchar(7) NOT NULL COMMENT 'YYYY-MM',
|
||||
`account_code` varchar(20) NOT NULL,
|
||||
`dept_id` int(11) DEFAULT NULL,
|
||||
`total_debit` decimal(15,2) NOT NULL DEFAULT 0.00,
|
||||
`total_credit` decimal(15,2) NOT NULL DEFAULT 0.00,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_balance` (`company_id`, `period`, `account_code`, `dept_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
|
||||
@@ -0,0 +1,39 @@
|
||||
-- db: wms2
|
||||
-- Seeds standard parent (header) COA accounts for every existing company.
|
||||
-- These are grouping-only accounts (is_posting=0); item accounts are added by each company.
|
||||
|
||||
INSERT IGNORE INTO `ac_account` (company_id, account_code, account_name, account_type, parent_code, is_posting, status)
|
||||
SELECT
|
||||
c.company_id,
|
||||
a.account_code,
|
||||
a.account_name,
|
||||
a.account_type,
|
||||
NULL,
|
||||
0,
|
||||
1
|
||||
FROM (
|
||||
SELECT '1000' AS account_code, 'Assets' AS account_name, 'asset' AS account_type UNION ALL
|
||||
SELECT '1100', 'Current Assets', 'asset' UNION ALL
|
||||
SELECT '1200', 'Accounts Receivable', 'asset' UNION ALL
|
||||
SELECT '1300', 'Inventory', 'asset' UNION ALL
|
||||
SELECT '1400', 'Other Current Assets', 'asset' UNION ALL
|
||||
SELECT '1500', 'Fixed Assets', 'asset' UNION ALL
|
||||
SELECT '2000', 'Liabilities', 'liability' UNION ALL
|
||||
SELECT '2100', 'Accounts Payable', 'liability' UNION ALL
|
||||
SELECT '2200', 'Output VAT Payable', 'liability' UNION ALL
|
||||
SELECT '2300', 'Other Current Liabilities', 'liability' UNION ALL
|
||||
SELECT '3000', 'Equity', 'equity' UNION ALL
|
||||
SELECT '3100', 'Share Capital', 'equity' UNION ALL
|
||||
SELECT '3200', 'Retained Earnings', 'equity' UNION ALL
|
||||
SELECT '4000', 'Revenue', 'revenue' UNION ALL
|
||||
SELECT '4100', 'Sales Revenue', 'revenue' UNION ALL
|
||||
SELECT '4200', 'Other Income', 'revenue' UNION ALL
|
||||
SELECT '5000', 'Cost of Goods Sold', 'expense' UNION ALL
|
||||
SELECT '6000', 'Operating Expenses', 'expense' UNION ALL
|
||||
SELECT '6100', 'Selling Expenses', 'expense' UNION ALL
|
||||
SELECT '6200', 'General & Administrative Expenses', 'expense' UNION ALL
|
||||
SELECT '7000', 'Financial Expenses', 'expense' UNION ALL
|
||||
SELECT '8000', 'Tax Expenses', 'expense' UNION ALL
|
||||
SELECT '9000', 'Other Expenses', 'expense'
|
||||
) a
|
||||
CROSS JOIN (SELECT DISTINCT company_id FROM ac_account UNION SELECT company_id FROM ac_period) c;
|
||||
@@ -0,0 +1,390 @@
|
||||
-- db: wms2
|
||||
-- Seed COA demo data from coa.xlsx (359 accounts)
|
||||
|
||||
INSERT IGNORE INTO ac_account
|
||||
(company_id, account_code, account_name, account_type, parent_code, is_posting, status)
|
||||
SELECT c.company_id, t.code, t.name, t.type, t.parent, t.posting, t.stat
|
||||
FROM (
|
||||
SELECT '100' AS code, 'เงินสดและรายการเทียบเท่าเงินสด' AS name, 'asset' AS type, NULL AS parent, 0 AS posting, 1 AS stat UNION ALL
|
||||
SELECT '1001000', 'เงินสด', 'asset', '100', 1, 1 UNION ALL
|
||||
SELECT '10010001', 'เงินสด', 'asset', '100', 1, 1 UNION ALL
|
||||
SELECT '10010012', 'สินทรัพย์', 'asset', '170', 1, 1 UNION ALL
|
||||
SELECT '1001100', 'เงินฝากระหว่างทาง', 'asset', '100', 1, 1 UNION ALL
|
||||
SELECT '1001200', 'เงินสดย่อย', 'asset', '100', 1, 1 UNION ALL
|
||||
SELECT '1001300', 'เงินในกระเป๋าเงินอิเล็กทรอนิคส์', 'asset', '100', 1, 1 UNION ALL
|
||||
SELECT '1001300-1', 'เงินในกระเป๋าเงินอิเล็กทรอนิคส์', 'asset', '100', 0, 1 UNION ALL
|
||||
SELECT '101', 'เงินฝากออมทรัพย์', 'asset', '100', 0, 1 UNION ALL
|
||||
SELECT '1011000', 'เงินฝากออมทรัพย์-ธนาคารกรุงเทพ', 'asset', '101', 1, 1 UNION ALL
|
||||
SELECT '1012000', 'เงินฝากออมทรัพย์-ธนาคารกสิกรไทย', 'asset', '101', 1, 1 UNION ALL
|
||||
SELECT '1013000', 'เงินฝากออมทรัพย์-ธนาคารไทยพาณิชย์', 'asset', '101', 1, 1 UNION ALL
|
||||
SELECT '1014000', 'เงินฝากออมทรัพย์-ธนาคารกรุงไทย', 'asset', '101', 1, 1 UNION ALL
|
||||
SELECT '1015000', 'เงินฝากออมทรัพย์-ธนาคารกรุงศรีอยุธยา', 'asset', '101', 1, 1 UNION ALL
|
||||
SELECT '1016000', 'เงินฝากออมทรัพย์-ธนาคารทหารไทย', 'asset', '101', 1, 1 UNION ALL
|
||||
SELECT '1017000', 'เงินฝากออมทรัพย์-ธนาคารธนชาติ', 'asset', '101', 1, 1 UNION ALL
|
||||
SELECT '1018000', 'เงินฝากออมทรัพย์-ธนาคารเพื่อการส่งออกและนำเข้าแห่งประเทศไทย', 'asset', '101', 1, 1 UNION ALL
|
||||
SELECT '102', 'เงินฝากกระแสรายวัน', 'asset', '100', 0, 1 UNION ALL
|
||||
SELECT '1021000', 'เงินฝากกระแสรายวัน-ธนาคารกรุงเทพ5890104321', 'asset', '102', 1, 1 UNION ALL
|
||||
SELECT '1022000', 'เงินฝากกระแสรายวัน-ธนาคารกสิกรไทย', 'asset', '102', 1, 1 UNION ALL
|
||||
SELECT '1023000', 'เงินฝากกระแสรายวัน-ธนาคารไทยพาณิชย์', 'asset', '102', 1, 1 UNION ALL
|
||||
SELECT '1024000', 'เงินฝากกระแสรายวัน-ธนาคารกรุงไทย3440105921', 'asset', '102', 1, 1 UNION ALL
|
||||
SELECT '1025000', 'เงินฝากกระแสรายวัน-ธนาคารกรุงศรีอยุธยา', 'asset', '102', 1, 1 UNION ALL
|
||||
SELECT '1026000', 'เงินฝากกระแสรายวัน-ธนาคารทหารไทย', 'asset', '102', 1, 1 UNION ALL
|
||||
SELECT '1027000', 'เงินฝากกระแสรายวัน-ธนาคารธนชาติ', 'asset', '102', 1, 1 UNION ALL
|
||||
SELECT '103', 'เงินฝากประจำ', 'asset', '100', 0, 1 UNION ALL
|
||||
SELECT '1031000', 'เงินฝากประจำ-ธนาคารกรุงเทพ', 'asset', '103', 1, 1 UNION ALL
|
||||
SELECT '1032000', 'เงินฝากประจำ-ธนาคารกสิกรไทย', 'asset', '103', 1, 1 UNION ALL
|
||||
SELECT '1033000', 'เงินฝากประจำ-ธนาคารไทยพาณิชย์', 'asset', '103', 1, 1 UNION ALL
|
||||
SELECT '1034000', 'เงินฝากประจำ-ธนาคารกรุงไทย', 'asset', '103', 1, 1 UNION ALL
|
||||
SELECT '1035000', 'เงินฝากประจำ-ธนาคารกรุงศรีอยุธยา', 'asset', '103', 1, 1 UNION ALL
|
||||
SELECT '1036000', 'เงินฝากประจำ-ธนาคารทหารไทย', 'asset', '103', 1, 1 UNION ALL
|
||||
SELECT '1037000', 'เงินฝากประจำ-ธนาคารธนชาติ', 'asset', '103', 1, 1 UNION ALL
|
||||
SELECT '11-12-01-01', 'CSI-T#1CUR.-BBL 147-3-10664-7', 'asset', NULL, 1, 1 UNION ALL
|
||||
SELECT '110', 'เงินลงทุนชั่วคราว', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '120', 'ลูกหนี้การค้าและตั๋วเงินรับ', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '1201000', 'ลูกหนี้การค้าในประเทศ', 'asset', '120', 1, 1 UNION ALL
|
||||
SELECT '1202000', 'ลูกหนี้การค้าต่างประเทศ', 'asset', '120', 1, 1 UNION ALL
|
||||
SELECT '1203000', 'ลูกหนี้การค้าอื่นๆ', 'asset', '120', 1, 1 UNION ALL
|
||||
SELECT '1203100', 'ลูกหนี้รอยกมา', 'asset', '120', 1, 1 UNION ALL
|
||||
SELECT '1203200', 'เงินสดจากการขายรอนำฝากธนาคาร', 'asset', '120', 1, 1 UNION ALL
|
||||
SELECT '1204000', 'เช็ครับลงวันที่ล่วงหน้า', 'asset', '120', 1, 1 UNION ALL
|
||||
SELECT '1204100', 'เช็คเงินฝากระหว่างทาง', 'asset', '120', 1, 1 UNION ALL
|
||||
SELECT '1204200', 'เช็คคืน', 'asset', '120', 1, 1 UNION ALL
|
||||
SELECT '1205000', 'เครดิตการ์ด', 'asset', '120', 1, 1 UNION ALL
|
||||
SELECT '1209000', 'ค่าเผื่อหนี้สงสัยจะสูญ', 'asset', '120', 1, 1 UNION ALL
|
||||
SELECT '121', 'เงินให้กู้ยืมระยะสั้น', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '122', 'ลูกหนี้เงินให้กู้ยืมแก่กรรมการและลูกจ้าง', 'asset', '121', 0, 1 UNION ALL
|
||||
SELECT '1221000', 'ลูกหนี้เงินให้กู้ยืมแก่กรรมการ', 'asset', '122', 1, 1 UNION ALL
|
||||
SELECT '1222000', 'ลูกหนี้เงินให้กู้ยืมแก่ลูกจ้าง', 'asset', '122', 1, 1 UNION ALL
|
||||
SELECT '1223000', 'ลูกหนี้เงินให้กู้ยืมแก่บริษัท.....', 'asset', '122', 1, 1 UNION ALL
|
||||
SELECT '129', 'ลูกหนี้อื่น', 'asset', '120', 0, 1 UNION ALL
|
||||
SELECT '1291000', 'ลูกหนี้อื่น', 'asset', '129', 1, 1 UNION ALL
|
||||
SELECT '1292000', 'ลูกหนี้รอยกมา', 'asset', '129', 1, 1 UNION ALL
|
||||
SELECT '130', 'สินค้าคงเหลือ', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '1301000', 'สินค้าสำเร็จรูป', 'asset', '130', 1, 1 UNION ALL
|
||||
SELECT '1302000', 'วัตถุดิบ', 'asset', '130', 1, 1 UNION ALL
|
||||
SELECT '1303000', 'วัสดุสิ้นเปลือง', 'asset', '130', 1, 1 UNION ALL
|
||||
SELECT '1304000', 'งานระหว่างทำ', 'asset', '130', 1, 1 UNION ALL
|
||||
SELECT '1309000', 'ค่าเผื่อสินค้าด้อยค่า', 'asset', '130', 1, 1 UNION ALL
|
||||
SELECT '140', 'ค่าใช้จ่ายจ่ายล่วงหน้า', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '1401000', 'ค่าใช้จ่ายจ่ายล่วงหน้า', 'asset', '140', 1, 1 UNION ALL
|
||||
SELECT '1402000', 'ภาษีเงินได้นิติบุคคลครึ่งปีภงด.51', 'asset', '140', 1, 1 UNION ALL
|
||||
SELECT '1403000', 'เงินมัดจำจ่ายล่วงหน้า', 'asset', '140', 1, 1 UNION ALL
|
||||
SELECT '1404000', 'ค่าประกันภัยท/สจ่ายล่วงหน้า', 'asset', '140', 1, 1 UNION ALL
|
||||
SELECT '141', 'เงินทดรองจ่าย/เงินมัดจำ', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '1411000', 'เงินทดรองจ่ายพนักงาน', 'asset', '141', 1, 1 UNION ALL
|
||||
SELECT '1412000', 'เงินทดรองจ่าย-บุคคลภายนอก', 'asset', '141', 1, 1 UNION ALL
|
||||
SELECT '1413000', 'เงินมัดจำระยะสั้น', 'asset', '141', 1, 1 UNION ALL
|
||||
SELECT '1414000', 'เงินประกันระยะสั้น', 'asset', '141', 1, 1 UNION ALL
|
||||
SELECT '142', 'รายได้ค้างรับ', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '1421000', 'รายได้ค่าขนส่งค้างรับ', 'asset', '142', 1, 1 UNION ALL
|
||||
SELECT '1422000', 'รายได้ค่าบริการค้างรับ', 'asset', '142', 1, 1 UNION ALL
|
||||
SELECT '143', 'ลูกหนี้สรรพากร', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '1431000', 'บัตรภาษี', 'asset', '143', 1, 1 UNION ALL
|
||||
SELECT '1432000', 'ภาษีซื้อ', 'asset', '143', 1, 1 UNION ALL
|
||||
SELECT '1433000', 'ภาษีซื้อยังไม่ถึงกำหนด', 'asset', '143', 1, 1 UNION ALL
|
||||
SELECT '1434000', 'ภาษีเงินได้ถูกหักณที่จ่าย', 'asset', '143', 1, 1 UNION ALL
|
||||
SELECT '150', 'สินทรัพย์หมุนเวียนอื่น', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '160', 'เงินลงทุน', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '1601000', 'พันธบัตร', 'asset', '160', 1, 1 UNION ALL
|
||||
SELECT '1602000', 'เงินลงทุนในบจก...', 'asset', '160', 1, 1 UNION ALL
|
||||
SELECT '1609000', 'เงินลงทุนระยะยาวอื่น', 'asset', '160', 1, 1 UNION ALL
|
||||
SELECT '161', 'เงินให้กู้ยืมระยะยาว', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '170', 'ที่ดินอาคารและอุปกรณ์', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '1701000', 'ที่ดิน', 'asset', '170', 1, 1 UNION ALL
|
||||
SELECT '1702000', 'อาคาร', 'asset', '170', 1, 1 UNION ALL
|
||||
SELECT '1702100', 'อาคาร-ส่วนปรับปรุ่งเพิ่ม', 'asset', '170', 1, 1 UNION ALL
|
||||
SELECT '1702200', 'อาคารโรงงาน', 'asset', '170', 1, 1 UNION ALL
|
||||
SELECT '1703000', 'เครื่องมือเครื่องใช้ในโรงงาน', 'asset', '170', 1, 1 UNION ALL
|
||||
SELECT '1703100', 'เครื่องจักร', 'asset', '170', 1, 1 UNION ALL
|
||||
SELECT '1704000', 'เครื่องใช้สำนักงาน', 'asset', '170', 1, 1 UNION ALL
|
||||
SELECT '1705000', 'คอมพิวเตอร์', 'asset', '170', 1, 1 UNION ALL
|
||||
SELECT '1706000', 'ยานพาหนะ', 'asset', '170', 1, 1 UNION ALL
|
||||
SELECT '171', 'ค่าเสื่อมราคาสะสม', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '1712000', 'ค่าเสื่อมราคาสะสม-อาคาร', 'asset', '171', 1, 1 UNION ALL
|
||||
SELECT '1712100', 'ค่าเสื่อมราคาสะสม–อาคารส่วนปรับปรุ่งเพิ่ม', 'asset', '171', 1, 1 UNION ALL
|
||||
SELECT '1712200', 'ค่าเสื่อมราคาสะสม–อาคารโรงงาน', 'asset', '171', 1, 1 UNION ALL
|
||||
SELECT '1713000', 'ค่าเสื่อมราคาสะสม–เครื่องมือเครื่องใช้ในโรงงาน', 'asset', '171', 1, 1 UNION ALL
|
||||
SELECT '1713100', 'ค่าเสื่อมราคาสะสม–เครื่องจักร', 'asset', '171', 1, 1
|
||||
) AS t
|
||||
CROSS JOIN (SELECT DISTINCT company_id FROM wms.company_map_user) AS c;
|
||||
|
||||
INSERT IGNORE INTO ac_account
|
||||
(company_id, account_code, account_name, account_type, parent_code, is_posting, status)
|
||||
SELECT c.company_id, t.code, t.name, t.type, t.parent, t.posting, t.stat
|
||||
FROM (
|
||||
SELECT '1714000' AS code, 'ค่าเสื่อมราคาสะสม–เครื่องใช้สำนักงาน' AS name, 'asset' AS type, '171' AS parent, 1 AS posting, 1 AS stat UNION ALL
|
||||
SELECT '1715000', 'ค่าเสื่อมราคาสะสม–ยานพาหนะ', 'asset', '171', 1, 1 UNION ALL
|
||||
SELECT '1716000', 'ค่าเสื่อมราคาสะสม–คอมพิวเตอร์', 'asset', '171', 1, 1 UNION ALL
|
||||
SELECT '180', 'สินทรัพย์ไม่มีตัวตน', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '1801000', 'ซอฟต์แวร์และแอฟพลิเคชั่น', 'asset', '180', 1, 1 UNION ALL
|
||||
SELECT '1802000', 'ค่าลิขสิทธิ์', 'asset', '180', 1, 1 UNION ALL
|
||||
SELECT '1803000', 'ค่าความนิยม', 'asset', '180', 1, 1 UNION ALL
|
||||
SELECT '1804000', 'ค่าสิทธิบัตร', 'asset', '180', 1, 1 UNION ALL
|
||||
SELECT '181', 'ค่าตัดจ่ายสะสม', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '1811000', 'ค่าตัดจำหน่ายสะสม–ซอฟต์แวร์และแอฟพลิเคชั่น', 'asset', '181', 1, 1 UNION ALL
|
||||
SELECT '1812000', 'ค่าตัดจำหน่ายสะสม–ค่าลิขสิทธิ์', 'asset', '181', 1, 1 UNION ALL
|
||||
SELECT '1813000', 'ค่าตัดจำหน่ายสะสม–ค่าความนิยม', 'asset', '181', 1, 1 UNION ALL
|
||||
SELECT '1814000', 'ค่าตัดจำหน่ายสะสม–ค่าสิทธิบัตร', 'asset', '181', 1, 1 UNION ALL
|
||||
SELECT '190', 'สินทรัพย์ไม่หมุนเวียนอื่น', 'asset', NULL, 0, 1 UNION ALL
|
||||
SELECT '200', 'เงินเบิกเกินบัญชีและเงินกู้ยืมระยะสั้นจากสถาบันการเงิน', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '2001000', 'เงินเบิกเกินบัญชีธนาคาร', 'liability', '200', 1, 1 UNION ALL
|
||||
SELECT '2002000', 'เงินกู้ยืมระยะสั้นบัญชีธนาคารและสถาบันการเงิน', 'liability', '200', 1, 1 UNION ALL
|
||||
SELECT '210', 'เจ้าหนี้การค้าและเจ้าหนี้อื่น', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '2101000', 'เจ้าหนี้การค้าในประเทศ', 'liability', '210', 1, 1 UNION ALL
|
||||
SELECT '2102000', 'เจ้าหนี้การค้าต่างประเทศ', 'liability', '210', 1, 1 UNION ALL
|
||||
SELECT '2103000', 'เจ้าหนี้การค้าอื่นๆ', 'liability', '210', 1, 1 UNION ALL
|
||||
SELECT '2103100', 'เจ้าหนี้รอยกมา', 'liability', '210', 1, 1 UNION ALL
|
||||
SELECT '2103200', 'เจ้าหนี้รอเคลีย์ริ่ง', 'liability', '210', 1, 1 UNION ALL
|
||||
SELECT '2103300', 'เจ้าหนี้สินค้ารับฝากขาย', 'liability', '210', 1, 1 UNION ALL
|
||||
SELECT '2103400', 'เจ้าหนี้เช่าซื้อบจก...', 'liability', '210', 1, 1 UNION ALL
|
||||
SELECT '2104000', 'เช็คจ่ายลงวันที่ล่วงหน้า', 'liability', '210', 1, 1 UNION ALL
|
||||
SELECT '2104100', 'เช็คจ่าย-รับคืน', 'liability', '210', 1, 1 UNION ALL
|
||||
SELECT '2105000', 'เครดิตการ์ด-ค้างจ่าย', 'liability', '210', 1, 1 UNION ALL
|
||||
SELECT '212404', 'เงินรับล่วงหน้า', 'liability', '233', 1, 1 UNION ALL
|
||||
SELECT '220', 'ส่วนของหนี้สินระยะยาวที่ถึงกำหนดชำระภายในหนึ่งปี', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '2211000', 'เงินกู้ยืมระยะยาว', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '2211101', 'เงินกู้ยืมระยะยาว-จากกรรมการ', 'liability', '2211000', 0, 1 UNION ALL
|
||||
SELECT '2211102', 'เงินกู้ยืมระยะยาว-จากกรรมการ', 'liability', '2211101', 1, 1 UNION ALL
|
||||
SELECT '230', 'เงินกู้ยืมระยะสั้น', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '2301000', 'เจ้าหนี้เงินกู้ยืมกรรมการ', 'liability', '230', 1, 1 UNION ALL
|
||||
SELECT '2302000', 'เจ้าหนี้เงินกู้ยืมบริษัทในเครือ', 'liability', '230', 1, 1 UNION ALL
|
||||
SELECT '231', 'ค่าใช้จ่ายค้างจ่าย', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '2311000', 'ค่าแรงงานค้างจ่าย', 'liability', '231', 1, 1 UNION ALL
|
||||
SELECT '2311100', 'เงินเดือนค้างจ่าย', 'liability', '231', 1, 1 UNION ALL
|
||||
SELECT '2311200', 'โบนัสและสวัสดิการค้างจ่าย', 'liability', '231', 1, 1 UNION ALL
|
||||
SELECT '2311300', 'ดอกเบี้ยค้างจ่าย', 'liability', '231', 1, 1 UNION ALL
|
||||
SELECT '2311400', 'ค่านายหน้าค้างจ่าย', 'liability', '231', 1, 1 UNION ALL
|
||||
SELECT '2311500', 'ค่าไฟฟ้าค้างจ่าย', 'liability', '231', 1, 1 UNION ALL
|
||||
SELECT '2311600', 'ค่าน้ำประปาค้างจ่าย', 'liability', '231', 1, 1 UNION ALL
|
||||
SELECT '2311700', 'ค่าโทรศัพท์ค้างจ่าย', 'liability', '231', 1, 1 UNION ALL
|
||||
SELECT '2311800', 'ค่าสอบบัญชีค้างจ่าย', 'liability', '231', 1, 1 UNION ALL
|
||||
SELECT '2311900', 'ค่าบริการทำบัญชีค้างจ่าย', 'liability', '231', 1, 1 UNION ALL
|
||||
SELECT '2312000', 'เงินประกันสังคมค้างจ่าย', 'liability', '231', 1, 1 UNION ALL
|
||||
SELECT '2312001', 'เงินประกันสังคมรอนำส่ง', 'liability', '231', 1, 1 UNION ALL
|
||||
SELECT '2312100', 'ค่าปรึกษากฎหมายค้างจ่าย', 'liability', '231', 1, 1 UNION ALL
|
||||
SELECT '2319000', 'ค่าใช้จ่ายค้างจ่ายอื่นๆ', 'liability', '231', 1, 1 UNION ALL
|
||||
SELECT '232', 'ภาษีค้างจ่าย', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '2320100', 'ภาษีหักณที่จ่ายภงด1', 'liability', '232', 1, 1 UNION ALL
|
||||
SELECT '2320300', 'ภาษีหักณที่จ่ายภงด3', 'liability', '232', 1, 1 UNION ALL
|
||||
SELECT '2325300', 'ภาษีหักณที่จ่ายภงด53', 'liability', '232', 1, 1 UNION ALL
|
||||
SELECT '2325400', 'ภาษีหักณที่จ่ายภงด54', 'liability', '232', 1, 1 UNION ALL
|
||||
SELECT '2328000', 'ภาษีเงินได้นิติบุคคลค้างจ่ายภงด50', 'liability', '232', 1, 1 UNION ALL
|
||||
SELECT '2329000', 'ภาษีอื่นค้างจ่าย', 'liability', '232', 1, 1 UNION ALL
|
||||
SELECT '233', 'รายได้รับล่วงหน้า', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '2331000', 'รายได้รับล่วงหน้า-ค่าสินค้า', 'liability', '233', 1, 1 UNION ALL
|
||||
SELECT '2332000', 'รายได้รับล่วงหน้า-ค่าบริการ', 'liability', '233', 1, 1 UNION ALL
|
||||
SELECT '2333000', 'รายได้รับล่วงหน้า-ค่านายหน้า', 'liability', '233', 1, 1 UNION ALL
|
||||
SELECT '2334000', 'เงินมัดจำรับล่วงหน้า', 'liability', '233', 1, 1 UNION ALL
|
||||
SELECT '234', 'เจ้าหนี้กรมสรรพากร', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '2341000', 'ภาษีขาย', 'liability', '234', 1, 1 UNION ALL
|
||||
SELECT '2342000', 'ภาษีขายยังไม่ถึงกำหนด', 'liability', '234', 1, 1 UNION ALL
|
||||
SELECT '2343000', 'เจ้าหนี้กรมสรรพากรภพ30', 'liability', '234', 1, 1 UNION ALL
|
||||
SELECT '238', 'ประมาณการหนี้สินระยะสั้น', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '239', 'หนี้สินหมุนเวียนอื่น', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '2391000', 'เงินสำรองเลี้ยงชีพค้างจ่าย', 'liability', '239', 1, 1 UNION ALL
|
||||
SELECT '240', 'เงินกู้ยืมระยะยาว', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '2401000', 'เงินกู้ยืมระยะยาวจากธนาคารกรุงเทพ', 'liability', '240', 1, 1 UNION ALL
|
||||
SELECT '2401100', 'เงินกู้ยืมระยะยาวจากธนาคารกสิกรไทย', 'liability', '240', 1, 1 UNION ALL
|
||||
SELECT '2401200', 'เงินกู้ยืมระยะยาวจากธนาคารไทยพาณิชย์', 'liability', '240', 1, 1 UNION ALL
|
||||
SELECT '2401300', 'เงินกู้ยืมระยะยาวจากธนาคารกรุงไทย', 'liability', '240', 1, 1 UNION ALL
|
||||
SELECT '2401400', 'เงินกู้ยืมระยะยาวจากธนาคารกรุงศรีอยุธยา', 'liability', '240', 1, 1 UNION ALL
|
||||
SELECT '2401500', 'เงินกู้ยืมระยะยาวจากธนาคารทหารไทย', 'liability', '240', 1, 1 UNION ALL
|
||||
SELECT '2401600', 'เงินกู้ยืมระยะยาวจากธนาคารธนชาติ', 'liability', '240', 1, 1 UNION ALL
|
||||
SELECT '2402000', 'เงินกู้ยืมระยะยาวจากสถาบันการเงิน......', 'liability', '240', 1, 1 UNION ALL
|
||||
SELECT '2403000', 'เงินกู้ยืมระยะยาวจากบริษัทในเครือ.....', 'liability', '240', 1, 1 UNION ALL
|
||||
SELECT '241', 'หนี้สินภาษีเงินได้รอการตัดบัญชี', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '242', 'ภาระผูกพันผลประโยชน์พนักงาน', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '243', 'ประมาณการหนี้สินระยะยาว', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '249', 'หนี้สินไม่หมุนเวียนอื่น', 'liability', NULL, 0, 1 UNION ALL
|
||||
SELECT '301', 'หุ้นสามัญ', 'equity', NULL, 0, 1 UNION ALL
|
||||
SELECT '3011000', 'หุ้นสามัญ–นายA', 'equity', '301', 1, 1 UNION ALL
|
||||
SELECT '3011001', 'หุ้นสามัญ–นายB', 'equity', '301', 1, 1 UNION ALL
|
||||
SELECT '3011002', 'หุ้นสามัญ–นายC', 'equity', '301', 1, 1 UNION ALL
|
||||
SELECT '301103', 'ทุนหุ้นสามัญ', 'equity', NULL, 1, 1 UNION ALL
|
||||
SELECT '302', 'หุ้นบุริมสิทธิ', 'equity', NULL, 0, 1 UNION ALL
|
||||
SELECT '303', 'กำไรสะสม', 'equity', NULL, 0, 1 UNION ALL
|
||||
SELECT '303100', 'กำไรสะสม', 'equity', '303', 1, 1 UNION ALL
|
||||
SELECT '3031000', 'กำไรสะสมจัดสรร', 'equity', '303', 1, 1 UNION ALL
|
||||
SELECT '3032000', 'กำไรสะสมที่ยังไม่ได้จัดสรร', 'equity', '303', 1, 1 UNION ALL
|
||||
SELECT '390', 'องค์ประกอบอื่นของส่วนของผู้ถือหุ้น', 'equity', NULL, 0, 1 UNION ALL
|
||||
SELECT '400', 'รายได้จากการขาย', 'revenue', NULL, 0, 1 UNION ALL
|
||||
SELECT '4001000', 'รายได้จากการขาย', 'revenue', '400', 1, 1 UNION ALL
|
||||
SELECT '4001004', 'ส่วนต่างราคาสินค้าCNF', 'revenue', NULL, 1, 1 UNION ALL
|
||||
SELECT '4002000', 'ส่วนลดการขาย', 'revenue', '400', 1, 1 UNION ALL
|
||||
SELECT '4003000', 'รับคืนสินค้า', 'revenue', '400', 1, 1
|
||||
) AS t
|
||||
CROSS JOIN (SELECT DISTINCT company_id FROM wms.company_map_user) AS c;
|
||||
|
||||
INSERT IGNORE INTO ac_account
|
||||
(company_id, account_code, account_name, account_type, parent_code, is_posting, status)
|
||||
SELECT c.company_id, t.code, t.name, t.type, t.parent, t.posting, t.stat
|
||||
FROM (
|
||||
SELECT '410' AS code, 'รายได้จากการบริการ' AS name, 'revenue' AS type, NULL AS parent, 0 AS posting, 1 AS stat UNION ALL
|
||||
SELECT '4100100', 'รายได้จากการบริการ', 'revenue', '410', 1, 1 UNION ALL
|
||||
SELECT '4100200', 'รายได้ค่าติดตั้ง', 'revenue', '410', 1, 1 UNION ALL
|
||||
SELECT '4100300', 'รายได้ค่าซ่อมแซม', 'revenue', '410', 1, 1 UNION ALL
|
||||
SELECT '4100400', 'รายได้ค่าเช่า', 'revenue', '410', 1, 1 UNION ALL
|
||||
SELECT '4111006', 'รายได้จากการขาย-ส่งเสริมการขาย', 'revenue', '400', 1, 1 UNION ALL
|
||||
SELECT '420', 'รายได้อื่น', 'revenue', NULL, 0, 1 UNION ALL
|
||||
SELECT '4200100', 'ดอกเบี้ยรับ', 'revenue', '420', 1, 1 UNION ALL
|
||||
SELECT '4200200', 'กำไร(ขาดทุน)จากการจำหน่ายทรัพย์สิน', 'revenue', '420', 1, 1 UNION ALL
|
||||
SELECT '4200300', 'ขายทรัพย์สินถาวร', 'revenue', '420', 1, 1 UNION ALL
|
||||
SELECT '4200400', 'ต้นทุนการขายทรัพย์สินถาวร', 'revenue', '420', 1, 1 UNION ALL
|
||||
SELECT '4200500', 'กำไร(ขาดทุน)จากอัตราแลกเปลี่ยน', 'revenue', '420', 1, 1 UNION ALL
|
||||
SELECT '4200600', 'รายได้จากการขายสินค้า/บริการให้บริษัทในเครือ', 'revenue', '420', 1, 1 UNION ALL
|
||||
SELECT '4200700', 'รายได้อื่น', 'revenue', '420', 1, 1 UNION ALL
|
||||
SELECT '510', 'ต้นทุนขายและหรือต้นทุนการให้บริการ', 'expense', NULL, 0, 1 UNION ALL
|
||||
SELECT '5101000', 'ต้นทุนสินค้าเพื่อขาย', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5101100', 'สินค้าสำเร็จรูปคงเหลือต้นงวด', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5101200', 'สินค้าสำเร็จรูปคงเหลือปลายงวด', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5101300', 'ซื้อสินค้า', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5101301', 'ซื้อผลไม้', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5101400', 'ค่าระวางและค่าขนส่ง', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5101500', 'ส่วนลดรับ', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5101600', 'ส่งคืนสินค้า', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5102000', 'ต้นทุนสินค้าเพื่อบริการ', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5102100', 'ต้นทุนวัสดุอุปกรณ์ใช้ไป', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5102200', 'ต้นทุนเงินเดือนและแรงงานทางตรง', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5102300', 'ต้นทุนแรงงานภายนอก', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5102400', 'โสหุ้ยการบริการ', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5103000', 'ซื้อวัตถุดิบ', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5103100', 'ส่งคืน-วัตถุดิบ', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5103200', 'ส่วนลดรับ-วัตถุดิบ', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5104000', 'ซื้อวัสดุสิ้นเปลือง', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5104100', 'ส่งคืน-วัสดุสิ้นเปลือง', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5104200', 'ส่วนลดรับ-วัสดุสิ้นเปลือง', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5105000', 'ต้นทุนแรงงานทางตรง', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5105100', 'ต้นทุนแรงงานภายนอก', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5105200', 'โสหุ้ยการผลิตทางตรง', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5105300', 'โสหุ้ยการผลิตทางอ้อม', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5105400', 'ปันส่วนต้นทุนค่าเสื่อมราคา', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5105500', 'ผลต่างต้นทุนมาตรฐาน', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5105600', 'ผลต่าง-วัตถุดิบ-ปลายงวด-ต้นงวด', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5105700', 'ผลต่าง-สินค้าคงเหลือ-ปลายงวด-ต้นงวด', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5105800', 'ผลต่าง-ต้นทุนค่าใช้จ่ายอื่น-ปลายงวด-ต้นงวด', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '5105900', 'ProvidendFund(SI)', 'expense', '510', 1, 1 UNION ALL
|
||||
SELECT '520', 'ค่าใช้จ่ายในการขาย', 'expense', NULL, 0, 1 UNION ALL
|
||||
SELECT '5200100', 'ค่าแรงและผลประโยชน์พนักงานขายชั่วคราว', 'expense', '520', 1, 1 UNION ALL
|
||||
SELECT '5200200', 'ค่านายหน้าและการส่งเสริมการขาย', 'expense', '520', 1, 1 UNION ALL
|
||||
SELECT '5200300', 'ค่านายหน้าและการส่งเสริมการขายบุคคลภายนอก', 'expense', '520', 1, 1 UNION ALL
|
||||
SELECT '5200500', 'ค่าโฆษณา', 'expense', '520', 1, 1 UNION ALL
|
||||
SELECT '5200600', 'ค่ารับรองขาย', 'expense', '520', 1, 1 UNION ALL
|
||||
SELECT '5200700', 'ค่าตอบแทนขายอื่น', 'expense', '520', 1, 1 UNION ALL
|
||||
SELECT '521', 'ค่าใช้จ่ายในการบริหาร', 'expense', NULL, 0, 1 UNION ALL
|
||||
SELECT '5210010', 'ค่าแรง', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210020', 'เงินเดือน', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210030', 'ค่าล่วงเวลา', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210040', 'ค่าสวัสดิการ', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210050', 'โบนัส', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210060', 'เงินเพิ่มพิเศษ', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210070', 'ค่าเดินทางและพักอาศัย', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210080', 'คอมมิชชั่น', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210090', 'เงินสมทบกองทุนประกันสังคม', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210100', 'เงินสมทบกองทุนทดแทน', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210110', 'ค่ารักษาพยาบาล', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210120', 'ค่าอบรมสัมมนา', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210130', 'ค่าแบบฟอร์มพนักงาน', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210140', 'ค่าเบี้ยประกันชีวิตและอุบัติเหตุ', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210150', 'ค่าอาหารและเครื่องดื่ม', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210160', 'ค่าจัดเลี้ยงและสันทนาการ', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210170', 'ค่าสวัสดิการอื่นๆ', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210180', 'ค่าทำบัญชี', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210190', 'ค่าสอบบัญชี', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210200', 'ค่าทนายและกฎหมาย', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210210', 'ค่าธรรมเนียมวิชาชีพอื่น', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210220', 'ค่าธรรมเนียมธนาคาร', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210230', 'ค่าธรรมเนียมใบอนุญาต', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210240', 'ค่าภาษีโรงเรือน', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210250', 'ค่าอากรแสตมป์', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210260', 'ค่าภาษียานพาหนะ', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210270', 'ภาษีป้าย', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210280', 'ค่าธรรมเนียมอื่น', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210290', 'ค่าที่ปรึกษา', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210300', 'ค่าเบี้ยประกัน', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210310', 'ค่าวัสดุและอุปกรณ์สิ้นเปลือง', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210320', 'ค่าเครื่องเขียนแบบพิมพ์', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210330', 'ค่าซ่อมแซมและบำรุงรักษา', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210340', 'วัสดุสิ้นเปลือง', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210350', 'ค่าวารสารและสมาชิก', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210360', 'ค่าเช่าสำนักงาน', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210370', 'ค่ารักษาความปลอดภัย', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210380', 'ค่ารักษาความสะอาด', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210390', 'ค่าใช้จ่ายส่วนกลาง', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210400', 'ค่าใช้จ่ายในการฝึกอบรม', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210410', 'ค่าสวัสดิการพนักงาน-ทั่วไป', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210420', 'ค่าซ่อมแซมบำรุงรักษา', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210430', 'ค่าจ้างและบริการทั่วไป', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210440', 'เงินบริจาค-เข้าเกณฑ์', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210450', 'ค่าใช้จ่ายเบ็ดเตล็ด', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5210460', 'ส่วนขาดเกินบัญชี', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '5211500', 'ProvidendFund(AD)', 'expense', '521', 1, 1 UNION ALL
|
||||
SELECT '522', 'ค่าสาธารณูปโภคและสื่อสาร', 'expense', NULL, 0, 1
|
||||
) AS t
|
||||
CROSS JOIN (SELECT DISTINCT company_id FROM wms.company_map_user) AS c;
|
||||
|
||||
INSERT IGNORE INTO ac_account
|
||||
(company_id, account_code, account_name, account_type, parent_code, is_posting, status)
|
||||
SELECT c.company_id, t.code, t.name, t.type, t.parent, t.posting, t.stat
|
||||
FROM (
|
||||
SELECT '5220010' AS code, 'ค่าโทรศัพท์' AS name, 'expense' AS type, '522' AS parent, 1 AS posting, 1 AS stat UNION ALL
|
||||
SELECT '5220020', 'ค่าไฟฟ้า', 'expense', '522', 1, 1 UNION ALL
|
||||
SELECT '5220030', 'ค่าน้ำประปา', 'expense', '522', 1, 1 UNION ALL
|
||||
SELECT '5220040', 'ค่าไปรษณีย์และอากรแสตมป์', 'expense', '522', 1, 1 UNION ALL
|
||||
SELECT '530', 'ค่าเสื่อมราคา', 'expense', NULL, 0, 1 UNION ALL
|
||||
SELECT '5301000', 'ค่าเสื่อมราคา-อาคาร', 'expense', '530', 1, 1 UNION ALL
|
||||
SELECT '5301100', 'ค่าเสื่อมราคา–อาคารส่วนปรับปรุ่งเพิ่ม', 'expense', '530', 1, 1 UNION ALL
|
||||
SELECT '5301200', 'ค่าเสื่อมราคา–อาคารโรงงาน', 'expense', '530', 1, 1 UNION ALL
|
||||
SELECT '5301300', 'ค่าเสื่อมราคา–เครื่องมือเครื่องใช้ในโรงงาน', 'expense', '530', 1, 1 UNION ALL
|
||||
SELECT '5301400', 'ค่าเสื่อมราคา–เครื่องจักร', 'expense', '530', 1, 1 UNION ALL
|
||||
SELECT '5301500', 'ค่าเสื่อมราคา–เครื่องใช้สำนักงาน', 'expense', '530', 1, 1 UNION ALL
|
||||
SELECT '5301600', 'ค่าเสื่อมราคา–ยานพาหนะ', 'expense', '530', 1, 1 UNION ALL
|
||||
SELECT '5301700', 'ค่าเสื่อมราคา–คอมพิวเตอร์', 'expense', '530', 1, 1 UNION ALL
|
||||
SELECT '531', 'ค่าตัดจ่าย', 'expense', NULL, 0, 1 UNION ALL
|
||||
SELECT '5310030', 'ค่าเบี้ยเลี้ยง', 'expense', '520', 1, 1 UNION ALL
|
||||
SELECT '5311000', 'ค่าตัดจำหน่าย–ซอฟต์แวร์และแอฟพลิเคชั่น', 'expense', '531', 1, 1 UNION ALL
|
||||
SELECT '5311100', 'ค่าตัดจำหน่าย–ค่าลิขสิทธิ์', 'expense', '531', 1, 1 UNION ALL
|
||||
SELECT '5311200', 'ค่าตัดจำหน่าย–ค่าความนิยม', 'expense', '531', 1, 1 UNION ALL
|
||||
SELECT '5311300', 'ค่าตัดจำหน่าย–ค่าสิทธิบัตร', 'expense', '531', 1, 1 UNION ALL
|
||||
SELECT '532', 'ปรับปรุงสำรองและการด้อยค่า', 'expense', NULL, 0, 1 UNION ALL
|
||||
SELECT '5321000', 'หนี้สูญ', 'expense', '532', 1, 1 UNION ALL
|
||||
SELECT '5321100', 'ปรับปรุงหนี้สงสัยจะสูญ', 'expense', '532', 1, 1 UNION ALL
|
||||
SELECT '5321200', 'ปรับปรุงสินค้าด้อยค่า', 'expense', '532', 1, 1 UNION ALL
|
||||
SELECT '5321300', 'ปรับปรุงการตั้งผลประโยชน์พนักงาน', 'expense', '532', 1, 1 UNION ALL
|
||||
SELECT '5321400', 'ขาดทุนจากอัตราแลกเปลี่ยน', 'expense', '532', 1, 1 UNION ALL
|
||||
SELECT '537008', 'ค่าใช้จ่ายในการจัดตั้งบริษัท', 'expense', NULL, 1, 1 UNION ALL
|
||||
SELECT '541', 'ค่าเบี้ยประกันภัย', 'expense', NULL, 0, 1 UNION ALL
|
||||
SELECT '5411000', 'ค่าเบี้ยประกัน-อาคาร', 'expense', '541', 1, 1 UNION ALL
|
||||
SELECT '5411100', 'ค่าเบี้ยประกัน-อุปกรณ์สำนักงาน', 'expense', '541', 1, 1 UNION ALL
|
||||
SELECT '5411200', 'ค่าเบี้ยประกัน-เครื่องตกแต่งสำนักงาน', 'expense', '541', 1, 1 UNION ALL
|
||||
SELECT '5411300', 'ค่าเบี้ยประกัน-ยานพาหนะ', 'expense', '541', 1, 1 UNION ALL
|
||||
SELECT '590', 'ค่าใช้จ่ายอื่นๆ', 'expense', NULL, 0, 1 UNION ALL
|
||||
SELECT '5901000', 'ดอกเบี้ยจ่าย', 'expense', '590', 1, 1 UNION ALL
|
||||
SELECT '5901100', 'หนี้สงสัยจะสูญ', 'expense', '590', 1, 1 UNION ALL
|
||||
SELECT '5901200', 'ค่ารับรอง', 'expense', '590', 1, 1 UNION ALL
|
||||
SELECT '5901300', 'ค่าบริจาคการกุศล', 'expense', '590', 1, 1 UNION ALL
|
||||
SELECT '5901400', 'ส่วนลดจากการขายลดเช็ค', 'expense', '590', 1, 1 UNION ALL
|
||||
SELECT '5901500', 'ค่าใช้จ่ายเบ็ดเตล็ด', 'expense', '590', 1, 1 UNION ALL
|
||||
SELECT '5901600', 'ค่าใช้จ่ายอื่นๆ', 'expense', '590', 1, 1 UNION ALL
|
||||
SELECT '5901700', 'หนี้สูญ', 'expense', '590', 1, 1 UNION ALL
|
||||
SELECT '5902000', 'ProvidendFund(Sales)', 'expense', '590', 1, 1 UNION ALL
|
||||
SELECT '591', 'รายจ่ายที่ไม่ถือเป็นรายจ่ายตามประมวลกฎหมายรัษฎากร', 'expense', NULL, 0, 1 UNION ALL
|
||||
SELECT '5911000', 'ค่าปรับภาษีอากร', 'expense', '591', 1, 1 UNION ALL
|
||||
SELECT '5911100', 'ภาษีซื้อไม่ขอคืน', 'expense', '591', 1, 1 UNION ALL
|
||||
SELECT '5911200', 'หนี้สูญเกินกฎหมายกำหนด', 'expense', '591', 1, 1 UNION ALL
|
||||
SELECT '5911300', 'เงินบริจาคไม่เข้าเกณฑ์', 'expense', '591', 1, 1 UNION ALL
|
||||
SELECT '5911400', 'ค่าใช้จ่ายไม่มีหลักฐาน', 'expense', '591', 1, 1 UNION ALL
|
||||
SELECT '5911500', 'ค่าใช้จ่ายที่ไม่สามารถระบุได้', 'expense', '591', 1, 1 UNION ALL
|
||||
SELECT '5911600', 'ค่าใช้จ่ายปีก่อน', 'expense', '591', 1, 1 UNION ALL
|
||||
SELECT '5919999', 'รายจ่ายยังไม่ได้แยกประเภท', 'expense', '591', 1, 1 UNION ALL
|
||||
SELECT '592', 'ภาษีเงินได้นิติบุคคลภงด50', 'expense', NULL, 0, 1 UNION ALL
|
||||
SELECT '5921000', 'ภาษีเงินได้นิติบุคคลภงด50', 'expense', '592', 1, 1 UNION ALL
|
||||
SELECT '900', 'สถิติการดำเนินงาน', 'expense', NULL, 0, 1 UNION ALL
|
||||
SELECT '9000001', 'ทดสอบ', 'expense', '900', 1, 1 UNION ALL
|
||||
SELECT '9001000', 'จำนวนลูกค้า', 'expense', '900', 1, 1 UNION ALL
|
||||
SELECT '9002000', 'ขนาดพื้นที่เช่า', 'expense', '900', 1, 1 UNION ALL
|
||||
SELECT '9003000', 'น้ำหนักขนส่ง', 'expense', '900', 1, 1 UNION ALL
|
||||
SELECT '9009999', 'บัญชีเคลียร์ตัวเลขสถิติ', 'expense', '900', 1, 1 UNION ALL
|
||||
SELECT '9999999', '9999999', 'expense', NULL, 1, 1
|
||||
) AS t
|
||||
CROSS JOIN (SELECT DISTINCT company_id FROM wms.company_map_user) AS c;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
-- db: wms2
|
||||
CREATE TABLE IF NOT EXISTS `ac_quotation` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`company_id` int(11) NOT NULL DEFAULT 0,
|
||||
`uuid` varchar(32) NOT NULL DEFAULT '',
|
||||
`quotation_number` varchar(50) NOT NULL DEFAULT '',
|
||||
`contact_id` int(11) NOT NULL DEFAULT 0,
|
||||
`quotation_date` date DEFAULT NULL,
|
||||
`valid_until` date DEFAULT NULL,
|
||||
`status` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`subtotal` decimal(18,4) NOT NULL DEFAULT 0.0000,
|
||||
`discount` decimal(18,4) NOT NULL DEFAULT 0.0000,
|
||||
`tax` decimal(18,4) NOT NULL DEFAULT 0.0000,
|
||||
`grand_total` decimal(18,4) NOT NULL DEFAULT 0.0000,
|
||||
`items` mediumtext NOT NULL DEFAULT '[]',
|
||||
`notes` text NOT NULL DEFAULT '',
|
||||
`log` mediumtext NOT NULL DEFAULT '{}',
|
||||
`created_at` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `company_id` (`company_id`),
|
||||
KEY `quotation_number` (`quotation_number`),
|
||||
KEY `status` (`status`),
|
||||
KEY `contact_id` (`contact_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- db: wms2
|
||||
ALTER TABLE td_order
|
||||
ADD COLUMN quotation_id int(11) NOT NULL DEFAULT 0 AFTER uuid,
|
||||
ADD KEY quotation_id (quotation_id);
|
||||
|
||||
ALTER TABLE ac_quotation
|
||||
ADD COLUMN order_id int(11) NOT NULL DEFAULT 0 AFTER status;
|
||||
Reference in New Issue
Block a user