Files
wms-app/app/accounting/posting_window.php
T
Thanakorn c60b705d98 Fix GL journal save, edit, detail view and list filters
Send journal lines, gl_id and list filters inside the ajax data payload where ajax_request reads them, select period when replacing a manual journal, and show ledger amounts to two decimals.
2026-09-18 16:19:27 +07:00

191 lines
7.2 KiB
PHP

<?php
session_start();
require '../config.php';
require '../include_header.php';
?>
<body>
<?php require '../include_topbar.php'; ?>
<?php require '../include_master_sidebar.php'; ?>
<main id="content" class="content py-15">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-6 gap-3">
<div>
<h1 class="fs-3 mb-1">Posting Window</h1>
<p class="mb-0">Define the date range within which GL postings and approved inventory entries can be created, edited, voided, or reversed</p>
</div>
</div>
</div>
</div>
<div class="row g-5">
<!-- Window card -->
<div class="col-lg-6">
<div class="card">
<div class="card-body p-6">
<div class="d-flex align-items-center gap-3 mb-5">
<div class="icon-shape icon-lg bg-primary bg-opacity-10 text-primary rounded-2">
<i class="ti ti-calendar-event fs-4"></i>
</div>
<div>
<h5 class="mb-0 fw-semibold">Open Posting Window</h5>
<p class="mb-0 text-muted small">Dates outside this range are locked for GL and inventory movement</p>
</div>
</div>
<!-- Current status -->
<div id="window_display" class="alert alert-secondary mb-5">Loading...</div>
<div class="alert alert-info d-flex gap-2 mb-5">
<i class="ti ti-info-circle mt-1"></i>
<div>
This window applies to both accounting GL postings and approved inventory entries.
Draft inventory rows can still be edited or cancelled before they affect stock balance.
</div>
</div>
<!-- Form -->
<div class="row g-3 mb-4">
<div class="col-sm-6">
<label class="form-label">Open From <span class="text-danger">*</span></label>
<input type="text" id="open_from" class="form-control">
</div>
<div class="col-sm-6">
<label class="form-label">Open To <span class="text-danger">*</span></label>
<input type="text" id="open_to" class="form-control">
</div>
<div class="col-12">
<div class="form-text">Example: <code>01/01/2026</code> to <code>31/12/2026</code> opens all of 2026.</div>
</div>
</div>
<div class="d-flex gap-2">
<button class="btn btn-primary" onclick="save_window()">
<i class="ti ti-device-floppy me-1"></i>Save Window
</button>
<button class="btn btn-outline-secondary" onclick="clear_window()">
<i class="ti ti-lock-open me-1"></i>Remove Restrictions
</button>
</div>
</div>
</div>
</div>
<!-- Guide card -->
<div class="col-lg-6">
<div class="card border-0 bg-light h-100">
<div class="card-body p-5">
<h6 class="fw-semibold mb-3"><i class="ti ti-info-circle me-1"></i>How it works</h6>
<ul class="small mb-0 ps-3">
<li class="mb-2">Journal entries, GL postings, and approved inventory movements are only allowed if their date falls <strong>within</strong> the open window.</li>
<li class="mb-2">Dates <strong>before Open From</strong> or <strong>after Open To</strong> are rejected.</li>
<li class="mb-2">Inventory entries that already affected stock balance use the same window when they are voided, cancelled, or reversed.</li>
<li class="mb-2">To open a full year: set <strong>01/01/YYYY</strong> → <strong>31/12/YYYY</strong>.</li>
<li class="mb-2">To roll the window forward (e.g. close January): change Open From to <strong>01/02/YYYY</strong>.</li>
<li class="mb-2"><strong>Remove Restrictions</strong> clears both dates — all dates become postable.</li>
<li>Existing posted GL and approved inventory entries are not changed automatically — the window is checked when a user creates, edits, voids, cancels, or reverses them.</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</main>
<?php require '../include_ending.php'; ?>
<script>
var open_from_picker = null;
var open_to_picker = null;
function init_window_pickers() {
var config = {
dateFormat: 'Y-m-d',
altInput: true,
altFormat: 'd/m/Y',
allowInput: true,
onReady: function(_, __, fp) {
fp.altInput.classList.add('form-control');
}
};
open_from_picker = flatpickr('#open_from', config);
open_to_picker = flatpickr('#open_to', config);
}
function load_window() {
ajax_request({
url: server_url + 'accounting/api/engine/posting_window.php',
autoPrepare: true,
checkRequired: 0,
action: 'read',
onSuccess: function(res) {
var d = res.output;
if (open_from_picker) open_from_picker.setDate(d.open_from || '', false, 'Y-m-d');
if (open_to_picker) open_to_picker.setDate(d.open_to || '', false, 'Y-m-d');
render_display(d.open_from, d.open_to);
}
});
}
function render_display(from, to) {
var el = document.getElementById('window_display');
if (!from && !to) {
el.className = 'alert alert-success mb-5';
el.innerHTML = '<i class="ti ti-lock-open me-2"></i><strong>No restrictions</strong> — all dates are open for posting.';
} else {
el.className = 'alert alert-primary mb-5';
var f = from ? '<strong>' + escape_html(from) + '</strong>' : '<span class="text-muted">no lower limit</span>';
var t = to ? '<strong>' + escape_html(to) + '</strong>' : '<span class="text-muted">no upper limit</span>';
el.innerHTML = '<i class="ti ti-calendar-event me-2"></i>Posting open from ' + f + ' to ' + t;
}
}
function save_window() {
var from = document.getElementById('open_from').value;
var to = document.getElementById('open_to').value;
ajax_request({
url: server_url + 'accounting/api/engine/posting_window.php',
autoPrepare: true,
checkRequired: 0,
action: 'save',
// Inside `data`: as top-level options these were ignored, and the save
// only worked because autoPrepare happens to sweep the two inputs, whose
// ids match the field names.
data: { open_from: from, open_to: to },
onSuccess: function() {
render_display(from, to);
}
});
}
function clear_window() {
if (open_from_picker) open_from_picker.clear();
if (open_to_picker) open_to_picker.clear();
ajax_request({
url: server_url + 'accounting/api/engine/posting_window.php',
autoPrepare: true,
checkRequired: 0,
action: 'save',
data: { open_from: '', open_to: '' },
onSuccess: function() {
render_display('', '');
}
});
}
init_window_pickers();
load_window();
</script>
</body>
</html>