Files
wms-app/app/setting/gl_maintenance.php

222 lines
8.2 KiB
PHP

<?php
require_once __DIR__ . '/../session.php';
require '../config.php';
require '../include_header.php';
?>
<body>
<?php require '../include_topbar.php'; ?>
<?php require '../include_setting_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">ETL GL Summary Maintenance</h1>
<p class="mb-0">Manages the <code>etl_gl_summary</code> table used by the accounting reports</p>
</div>
</div>
</div>
</div>
<div class="row g-5">
<!-- Automatic Maintenance -->
<div class="col-12 col-lg-6">
<div class="card border-0 shadow-sm h-100" style="position:relative;">
<div class="card-header bg-transparent border-bottom">
<h6 class="mb-0"><i class="ti ti-clock me-2"></i>Automatic Maintenance</h6>
</div>
<div class="card-body">
<p class="text-muted small mb-4">
The system automatically checks and repairs the GL summary table nightly.
Each company is assigned a fixed time slot to distribute load across the day.
</p>
<table class="table table-sm table-borderless mb-0">
<tbody>
<tr>
<td class="text-muted small" style="width:160px;">Scheduled slot</td>
<td><strong id="stat_slot">—</strong></td>
</tr>
<tr>
<td class="text-muted small">Last ran</td>
<td id="stat_last_ran">—</td>
</tr>
<tr>
<td class="text-muted small">Last result</td>
<td id="stat_last_result">—</td>
</tr>
<tr>
<td class="text-muted small">Gaps found</td>
<td id="stat_gaps_found">—</td>
</tr>
<tr>
<td class="text-muted small">Periods rebuilt</td>
<td id="stat_periods_rebuilt">—</td>
</tr>
<tr>
<td class="text-muted small">Next run</td>
<td id="stat_next_run">—</td>
</tr>
<tr><td colspan="2"><hr class="my-2"></td></tr>
<tr>
<td class="text-muted small">Current gaps</td>
<td id="stat_current_gaps">
<span class="spinner-border spinner-border-sm text-secondary"></span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="card-loader-overlay" id="overlay_status">
<div class="spinner-border spinner-border-sm text-primary"></div>
</div>
</div>
</div>
<!-- Manual Rebuild -->
<div class="col-12 col-lg-6">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-transparent border-bottom">
<h6 class="mb-0"><i class="ti ti-database me-2"></i>Manual Full Rebuild</h6>
</div>
<div class="card-body d-flex flex-column">
<p class="text-muted small mb-3">
Wipes and rebuilds the entire GL summary table from raw journal entries.
Use this after a direct database fix, data migration, or if dashboard figures look incorrect.
</p>
<div class="alert alert-warning d-flex gap-2 align-items-start py-2 px-3 small mb-4">
<i class="ti ti-alert-triangle mt-1 flex-shrink-0"></i>
<span>Rebuilds one month at a time. The dashboard stays usable during the rebuild — it falls back to raw data for any period not yet in the summary.</span>
</div>
<div class="mt-auto">
<button class="btn btn-danger" id="btn_rebuild_full">
<i class="ti ti-database-import me-1"></i>Rebuild Full ETL Table
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<?php require '../include_ending.php'; ?>
<script>
function load_status() {
$('#overlay_status').show();
ajax_request({
url: server_url + 'setting/api/engine/gl_maintenance.php',
autoPrepare: true, checkRequired: 0, noLoading: true, queueLock: false, action: 'read',
onSuccess: function(res) {
var d = res.output;
$('#stat_slot').text(pad2(d.slot_hour) + ':00 daily');
$('#stat_last_ran').text(d.last_ran || '—');
$('#stat_last_result').html(
d.last_result === 'ok'
? '<span class="badge bg-success-subtle text-success">OK</span>'
: d.last_result
? '<span class="badge bg-danger-subtle text-danger">' + escape_html(d.last_result) + '</span>'
: '—'
);
$('#stat_gaps_found').text(d.last_result ? d.gaps_found : '—');
$('#stat_periods_rebuilt').text(d.last_result ? d.periods_rebuilt : '—');
$('#stat_next_run').text(d.next_run || '—');
$('#overlay_status').hide();
}
});
}
function pad2(n) { return String(n).padStart(2, '0'); }
$('#btn_rebuild_full').on('click', function() {
bootbox.confirm({
title: 'Rebuild Full ETL Table',
message: 'This will rebuild the GL summary table one month at a time from raw journal entries. Continue?',
buttons: {
confirm: { label: 'Rebuild', className: 'btn-danger' },
cancel: { label: 'Cancel', className: 'btn-secondary' }
},
callback: function(confirmed) {
if (!confirmed) return;
var btn = $('#btn_rebuild_full').prop('disabled', true);
ajax_request({
url: server_url + 'setting/api/engine/gl_maintenance.php',
autoPrepare: true, checkRequired: 0, noLoading: true, queueLock: false, action: 'get_periods',
onSuccess: function(res) {
var periods = res.output || [];
var total = periods.length;
if (total === 0) {
btn.prop('disabled', false);
bootbox.alert('No GL periods found.');
return;
}
BatchOverlay.show('Rebuilding GL Summary', total, { unit: 'periods', color: '#dc3545' });
function rebuild_next(i) {
if (i >= total) {
BatchOverlay.done(total + ' period(s) rebuilt successfully.', function() {
btn.prop('disabled', false);
});
load_status();
load_current_gaps();
return;
}
BatchOverlay.update(i, total, periods[i]);
ajax_request({
url: server_url + 'setting/api/engine/gl_maintenance.php',
autoPrepare: true, checkRequired: 0, noLoading: true, queueLock: false,
action: 'rebuild_period', data: { period: periods[i] },
onSuccess: function() { rebuild_next(i + 1); },
onError: function() { rebuild_next(i + 1); }
});
}
rebuild_next(0);
},
onError: function() {
BatchOverlay.hide();
btn.prop('disabled', false);
}
});
}
});
});
function load_current_gaps() {
ajax_request({
url: server_url + 'setting/api/engine/gl_maintenance.php',
autoPrepare: true, checkRequired: 0, noLoading: true, queueLock: false, action: 'get_current_gaps',
onSuccess: function(res) {
var n = res.output.current_gaps;
$('#stat_current_gaps').html(
n === 0
? '<span class="badge bg-success-subtle text-success">0 — up to date</span>'
: '<span class="badge bg-warning-subtle text-warning">' + n + ' period(s) need repair</span>'
);
},
onError: function() {
$('#stat_current_gaps').text('—');
}
});
}
load_status();
load_current_gaps();
</script>
</body>
</html>