This commit is contained in:
Flatlogic Bot 2026-05-03 09:01:56 +00:00
parent fe06e618d4
commit 06c852731f

518
index.php
View File

@ -1,6 +1,154 @@
<?php
declare(strict_types=1);
$GLOBALS['app_runtime_debug_stage'] = 'boot:script_loaded';
$GLOBALS['app_runtime_debug_timeline'] = [
['time' => date('H:i:s'), 'stage' => 'boot:script_loaded'],
];
$GLOBALS['app_runtime_debug_rendered'] = false;
if (!function_exists('runtime_debug_boot_mark')) {
function runtime_debug_boot_mark(string $stage, array $context = []): void {
$GLOBALS['app_runtime_debug_stage'] = $stage;
$timeline = $GLOBALS['app_runtime_debug_timeline'] ?? [];
$entry = [
'time' => date('H:i:s'),
'stage' => $stage,
];
if ($context !== []) {
$entry['context'] = $context;
}
$timeline[] = $entry;
if (count($timeline) > 20) {
$timeline = array_slice($timeline, -20);
}
$GLOBALS['app_runtime_debug_timeline'] = $timeline;
}
}
if (!function_exists('runtime_debug_boot_force_details')) {
function runtime_debug_boot_force_details(): bool {
$candidates = [
$_GET['debug'] ?? null,
$_GET['app_debug'] ?? null,
getenv('APP_RUNTIME_DEBUG'),
$_ENV['APP_RUNTIME_DEBUG'] ?? null,
$_SERVER['APP_RUNTIME_DEBUG'] ?? null,
];
foreach ($candidates as $candidate) {
if ($candidate === false || $candidate === null) {
continue;
}
$value = strtolower(trim((string)$candidate));
if (in_array($value, ['1', 'true', 'yes', 'on'], true)) {
return true;
}
}
return false;
}
}
if (!defined('APP_RUNTIME_DEBUG_BOOTSTRAP_SHUTDOWN_REGISTERED')) {
define('APP_RUNTIME_DEBUG_BOOTSTRAP_SHUTDOWN_REGISTERED', true);
register_shutdown_function(static function (): void {
if (!empty($GLOBALS['app_runtime_debug_rendered']) || defined('APP_RUNTIME_DEBUG_FATAL_HANDLER_REGISTERED')) {
return;
}
$error = error_get_last();
if (!is_array($error)) {
return;
}
$fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR];
if (!in_array((int)($error['type'] ?? 0), $fatalTypes, true)) {
return;
}
$stage = (string)($GLOBALS['app_runtime_debug_stage'] ?? 'unknown');
$timeline = $GLOBALS['app_runtime_debug_timeline'] ?? [];
$parts = [
date('Y-m-d H:i:s'),
'[FatalError]',
(string)($error['message'] ?? 'Fatal error'),
'file=' . (string)($error['file'] ?? 'unknown') . ':' . (int)($error['line'] ?? 0),
'page=' . (string)($_GET['page'] ?? 'dashboard'),
'uri=' . (string)($_SERVER['REQUEST_URI'] ?? 'cli'),
'stage=' . $stage,
];
if ($timeline !== []) {
$parts[] = 'timeline=' . json_encode(array_slice($timeline, -8), JSON_UNESCAPED_UNICODE);
}
@file_put_contents(__DIR__ . '/runtime_debug.log', implode(' || ', $parts) . PHP_EOL, FILE_APPEND);
if (!headers_sent()) {
http_response_code(500);
header('Content-Type: text/html; charset=UTF-8');
header('X-Robots-Tag: noindex, nofollow');
}
$roleName = (string)($_SESSION['user_role_name'] ?? '');
$showDetails = runtime_debug_boot_force_details()
|| PHP_SAPI === 'cli'
|| strcasecmp($roleName, 'Administrator') === 0
|| (int)($_SESSION['user_id'] ?? 0) === 1;
$message = (string)($error['message'] ?? 'Fatal error');
$safeMessage = $showDetails
? $message
: 'A fatal application error occurred before the page finished loading.';
$timelineText = json_encode(array_slice($timeline, -8), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
$GLOBALS['app_runtime_debug_rendered'] = true;
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<title>Application Boot Error</title>
<style>
body { margin: 0; font-family: Inter, Arial, sans-serif; background: #f6f8fb; color: #1f2937; }
.wrap { max-width: 860px; margin: 48px auto; padding: 0 20px; }
.card { background: #fff; border-radius: 18px; box-shadow: 0 18px 60px rgba(15, 23, 42, 0.08); padding: 28px; }
h1 { margin: 0 0 12px; font-size: 28px; }
.meta { margin: 12px 0; color: #374151; }
.meta strong { color: #111827; }
.hint { margin-top: 18px; padding: 14px 16px; border-radius: 14px; background: #fff7ed; color: #9a3412; border: 1px solid #fed7aa; }
pre { margin: 18px 0 0; padding: 16px; background: #0f172a; color: #e2e8f0; border-radius: 14px; overflow: auto; font-size: 12px; line-height: 1.5; }
</style>
</head>
<body>
<main class="wrap">
<section class="card">
<h1>Application Boot Error</h1>
<p>The request failed before the main page finished loading.</p>
<div class="meta"><strong>Stage:</strong> <?= htmlspecialchars($stage) ?></div>
<div class="meta"><strong>Message:</strong> <?= htmlspecialchars($safeMessage) ?></div>
<div class="hint">A copy of this failure was written to <code>runtime_debug.log</code>.</div>
<?php if ($showDetails && $timelineText !== false && $timelineText !== '[]'): ?>
<pre><?= htmlspecialchars((string)$timelineText) ?></pre>
<?php endif; ?>
</section>
</main>
</body>
</html>
<?php
exit;
});
}
runtime_debug_boot_mark('boot:session_setup');
// Sessions setup
$sessions_dir = __DIR__ . '/sessions';
if (!is_dir($sessions_dir)) {
@ -23,6 +171,8 @@ if (!empty($missing_extensions)) {
die("Error: The following PHP extensions are required but missing: " . implode(', ', $missing_extensions) . ". Please contact your hosting provider to enable them.");
}
runtime_debug_boot_mark('boot:extensions_ready');
// Enhanced session security and iframe compatibility
if ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')) {
session_set_cookie_params([
@ -35,12 +185,15 @@ if ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') || (isset($_SERVER[
}
session_start();
runtime_debug_boot_mark('boot:session_started');
require_once __DIR__ . '/includes/page_routes.php';
if (!defined('APP_ROUTE_BOOTSTRAP')) {
page_redirect_legacy_url();
}
runtime_debug_boot_mark('boot:routes_ready');
if (!function_exists('app_file_debug_logging_enabled')) {
function app_file_debug_logging_enabled(): bool {
static $enabled = null;
@ -104,12 +257,15 @@ if (isset($_GET['action']) && $_GET['action'] === 'download_items_template') {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
app_debug_file_log('post_debug.log', date('Y-m-d H:i:s') . " - POST: " . json_encode($_POST));
}
runtime_debug_boot_mark('boot:loading_core_dependencies');
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/includes/SimpleXLSX.php';
require_once __DIR__ . '/includes/stock_helper.php';
require_once __DIR__ . '/includes/wablas_helper.php';
require_once __DIR__ . '/db/BackupService.php';
runtime_debug_boot_mark('boot:core_dependencies_loaded');
// Helper for current outlet
if (!function_exists('current_outlet_id')) {
function current_outlet_id() {
@ -362,8 +518,175 @@ if (!function_exists('line_item_vat_amount')) {
}
}
if (!function_exists('runtime_debug_mark')) {
function runtime_debug_mark(string $stage, array $context = []): void {
runtime_debug_boot_mark($stage, $context);
}
}
if (!function_exists('runtime_debug_current_stage')) {
function runtime_debug_current_stage(): string {
return (string)($GLOBALS['app_runtime_debug_stage'] ?? 'unknown');
}
}
if (!function_exists('runtime_debug_recent_timeline')) {
function runtime_debug_recent_timeline(int $limit = 8): array {
$timeline = $GLOBALS['app_runtime_debug_timeline'] ?? [];
if (!is_array($timeline)) {
return [];
}
return array_slice($timeline, -max(1, $limit));
}
}
if (!function_exists('runtime_debug_force_details_enabled')) {
function runtime_debug_force_details_enabled(): bool {
return runtime_debug_boot_force_details();
}
}
if (!function_exists('runtime_debug_require')) {
function runtime_debug_require(string $file, array $context = []): void {
runtime_debug_mark('require:' . basename($file), $context + ['file' => $file]);
require $file;
}
}
if (!function_exists('runtime_debug_extract_table_name')) {
function runtime_debug_extract_table_name(Throwable $throwable): ?string {
$message = $throwable->getMessage();
$patterns = [
"/Table '[^']+\.([^']+)' doesn't exist/i",
'/(?:INSERT\s+INTO|UPDATE|FROM|JOIN|DELETE\s+FROM|ALTER\s+TABLE)\s+`?([a-zA-Z0-9_]+)`?/i',
];
foreach ($patterns as $pattern) {
if (preg_match($pattern, $message, $matches)) {
return (string)$matches[1];
}
}
return null;
}
}
if (!function_exists('runtime_debug_extract_column_name')) {
function runtime_debug_extract_column_name(Throwable $throwable): ?string {
$message = $throwable->getMessage();
if (preg_match("/Unknown column '([^']+)'/i", $message, $matches)) {
return (string)$matches[1];
}
if (preg_match('/Column not found: [0-9]+ ([a-zA-Z0-9_]+)/i', $message, $matches)) {
return (string)$matches[1];
}
return null;
}
}
if (!function_exists('runtime_debug_find_related_migrations')) {
function runtime_debug_find_related_migrations(?string $tableName, ?string $columnName = null, int $limit = 6): array {
$directory = __DIR__ . '/db/migrations';
if (!is_dir($directory)) {
return [];
}
$needles = array_values(array_filter([
$tableName ? strtolower($tableName) : null,
$columnName ? strtolower($columnName) : null,
]));
if ($needles === []) {
return [];
}
$matches = [];
foreach (glob($directory . '/*.{sql,php}', GLOB_BRACE) ?: [] as $migrationPath) {
if (!is_readable($migrationPath)) {
continue;
}
$contents = @file_get_contents($migrationPath);
if ($contents === false) {
continue;
}
$haystack = strtolower($contents);
$matched = true;
foreach ($needles as $needle) {
if (!str_contains($haystack, $needle)) {
$matched = false;
break;
}
}
if ($matched) {
$matches[] = basename($migrationPath);
if (count($matches) >= $limit) {
break;
}
}
}
return $matches;
}
}
if (!function_exists('runtime_debug_schema_snapshot')) {
function runtime_debug_schema_snapshot(Throwable $throwable): array {
$tableName = runtime_debug_extract_table_name($throwable);
$columnName = runtime_debug_extract_column_name($throwable);
$snapshot = [
'table' => $tableName,
'column' => $columnName,
'table_exists' => null,
'columns' => [],
'database_error' => null,
'related_migrations' => runtime_debug_find_related_migrations($tableName, $columnName),
];
if ($tableName === null) {
return $snapshot;
}
try {
$pdo = db();
$snapshot['table_exists'] = db_table_exists($tableName);
if ($snapshot['table_exists']) {
$stmt = $pdo->prepare(
"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION"
);
$stmt->execute([$tableName]);
$snapshot['columns'] = array_map('strval', $stmt->fetchAll(PDO::FETCH_COLUMN) ?: []);
}
} catch (Throwable $e) {
$snapshot['database_error'] = $e->getMessage();
}
return $snapshot;
}
}
if (!function_exists('runtime_debug_is_fatal_error')) {
function runtime_debug_is_fatal_error($error): bool {
if (!is_array($error)) {
return false;
}
return in_array((int)($error['type'] ?? 0), [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR], true);
}
}
if (!function_exists('runtime_debug_can_render_details')) {
function runtime_debug_can_render_details(): bool {
if (runtime_debug_force_details_enabled()) {
return true;
}
if (PHP_SAPI === 'cli') {
return true;
}
@ -412,6 +735,7 @@ if (!function_exists('runtime_debug_log')) {
'page=' . ($_GET['page'] ?? 'dashboard'),
'uri=' . ($_SERVER['REQUEST_URI'] ?? 'cli'),
'user_id=' . (string)($_SESSION['user_id'] ?? 0),
'stage=' . runtime_debug_current_stage(),
];
$hint = runtime_debug_infer_schema_hint($throwable);
@ -423,8 +747,12 @@ if (!function_exists('runtime_debug_log')) {
$parts[] = 'error_info=' . json_encode($throwable->errorInfo, JSON_UNESCAPED_UNICODE);
}
$trace = explode("
", $throwable->getTraceAsString());
$timeline = runtime_debug_recent_timeline(10);
if ($timeline !== []) {
$parts[] = 'timeline=' . json_encode($timeline, JSON_UNESCAPED_UNICODE);
}
$trace = explode("\n", $throwable->getTraceAsString());
if ($trace !== []) {
$parts[] = 'trace=' . implode(' | ', array_slice($trace, 0, 5));
}
@ -435,6 +763,17 @@ if (!function_exists('runtime_debug_log')) {
if (!function_exists('runtime_debug_render_exception')) {
function runtime_debug_render_exception(Throwable $throwable): void {
if (!empty($GLOBALS['app_runtime_debug_rendered'])) {
if (!headers_sent()) {
http_response_code(500);
header('Content-Type: text/plain; charset=UTF-8');
}
echo 'Application Error';
exit;
}
$GLOBALS['app_runtime_debug_rendered'] = true;
runtime_debug_log($throwable);
$showDetails = runtime_debug_can_render_details();
@ -451,14 +790,19 @@ if (!function_exists('runtime_debug_render_exception')) {
$hint = runtime_debug_infer_schema_hint($throwable);
$requestUri = (string)($_SERVER['REQUEST_URI'] ?? 'cli');
$page = (string)($_GET['page'] ?? 'dashboard');
$tracePreview = array_slice(explode("
", $throwable->getTraceAsString()), 0, 8);
$traceText = implode("
", $tracePreview);
$currentStage = runtime_debug_current_stage();
$timeline = runtime_debug_recent_timeline(10);
$timelineText = json_encode($timeline, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
$schemaSnapshot = runtime_debug_schema_snapshot($throwable);
$tableExistsText = $schemaSnapshot['table_exists'] === null ? 'Unknown' : ($schemaSnapshot['table_exists'] ? 'Yes' : 'No');
$schemaColumnsText = !empty($schemaSnapshot['columns']) ? implode(', ', $schemaSnapshot['columns']) : '—';
$migrationText = !empty($schemaSnapshot['related_migrations']) ? implode(', ', $schemaSnapshot['related_migrations']) : '—';
$tracePreview = array_slice(explode("\n", $throwable->getTraceAsString()), 0, 8);
$traceText = implode("\n", $tracePreview);
$title = $showDetails ? 'Application Debug' : 'Application Error';
$summary = $showDetails
? 'The request failed. The details below should help identify the missing table or column.'
: 'An unexpected error occurred while loading this page.';
? 'The request failed. The details below should help identify the missing table, column, or view file.'
: 'An unexpected error occurred while loading this page. The diagnostic snapshot below may still point to what is missing.';
?>
<!doctype html>
<html lang="en">
@ -469,9 +813,10 @@ if (!function_exists('runtime_debug_render_exception')) {
<title><?= htmlspecialchars($title) ?></title>
<style>
body { margin: 0; font-family: Inter, Arial, sans-serif; background: #f6f8fb; color: #1f2937; }
.wrap { max-width: 900px; margin: 48px auto; padding: 0 20px; }
.wrap { max-width: 980px; margin: 48px auto; padding: 0 20px; }
.card { background: #fff; border-radius: 18px; box-shadow: 0 18px 60px rgba(15, 23, 42, 0.08); padding: 28px; }
h1 { margin: 0 0 10px; font-size: 28px; }
h2 { margin: 28px 0 10px; font-size: 18px; color: #111827; }
p { color: #4b5563; line-height: 1.6; }
.badge { display: inline-block; padding: 6px 10px; border-radius: 999px; background: #fee2e2; color: #991b1b; font-weight: 600; font-size: 13px; }
.grid { display: grid; grid-template-columns: 180px 1fr; gap: 12px 16px; margin-top: 22px; }
@ -493,7 +838,43 @@ if (!function_exists('runtime_debug_render_exception')) {
<h1><?= htmlspecialchars($title) ?></h1>
<p><?= htmlspecialchars($summary) ?></p>
<?php if ($hint !== null): ?>
<div class="hint"><strong>Possible issue:</strong> <?= htmlspecialchars($hint) ?></div>
<?php endif; ?>
<div class="grid">
<div class="label">Last boot step</div>
<div class="value"><?= htmlspecialchars($currentStage) ?></div>
<div class="label">Page</div>
<div class="value"><?= htmlspecialchars($page) ?></div>
<div class="label">Request URI</div>
<div class="value"><?= htmlspecialchars($requestUri) ?></div>
<?php if (!empty($schemaSnapshot['table'])): ?>
<div class="label">Detected table</div>
<div class="value"><?= htmlspecialchars((string)$schemaSnapshot['table']) ?></div>
<div class="label">Table exists</div>
<div class="value"><?= htmlspecialchars($tableExistsText) ?></div>
<?php endif; ?>
<?php if (!empty($schemaSnapshot['column'])): ?>
<div class="label">Detected column</div>
<div class="value"><?= htmlspecialchars((string)$schemaSnapshot['column']) ?></div>
<?php endif; ?>
<?php if (!empty($schemaSnapshot['columns'])): ?>
<div class="label">Existing columns</div>
<div class="value"><?= htmlspecialchars($schemaColumnsText) ?></div>
<?php endif; ?>
<?php if (!empty($schemaSnapshot['related_migrations'])): ?>
<div class="label">Likely migration</div>
<div class="value"><?= htmlspecialchars($migrationText) ?></div>
<?php endif; ?>
<?php if (!empty($schemaSnapshot['database_error'])): ?>
<div class="label">DB snapshot error</div>
<div class="value"><?= htmlspecialchars((string)$schemaSnapshot['database_error']) ?></div>
<?php endif; ?>
</div>
<?php if ($showDetails): ?>
<h2>Full exception</h2>
<div class="grid">
<div class="label">Exception</div>
<div class="value"><?= htmlspecialchars(get_class($throwable)) ?></div>
@ -503,31 +884,24 @@ if (!function_exists('runtime_debug_render_exception')) {
<div class="value"><?= htmlspecialchars($throwable->getFile()) ?></div>
<div class="label">Line</div>
<div class="value"><?= (int)$throwable->getLine() ?></div>
<div class="label">Page</div>
<div class="value"><?= htmlspecialchars($page) ?></div>
<div class="label">Request URI</div>
<div class="value"><?= htmlspecialchars($requestUri) ?></div>
</div>
<?php if ($hint !== null): ?>
<div class="hint"><strong>Schema hint:</strong> <?= htmlspecialchars($hint) ?></div>
<?php endif; ?>
<?php if ($traceText !== ''): ?>
<pre><?= htmlspecialchars($traceText) ?></pre>
<?php endif; ?>
<div class="actions">
<a class="btn btn-primary" href="schema_debug.php">Open schema debug report</a>
<a class="btn btn-secondary" href="index.php">Retry dashboard</a>
</div>
<p class="note">A copy of this failure was written to <code>runtime_debug.log</code>.</p>
<?php else: ?>
<div class="actions">
<a class="btn btn-secondary" href="index.php">Back to home</a>
</div>
<?php endif; ?>
<?php if ($timelineText !== false && $timelineText !== '[]'): ?>
<h2>Boot timeline</h2>
<pre><?= htmlspecialchars((string)$timelineText) ?></pre>
<?php endif; ?>
<div class="actions">
<a class="btn btn-primary" href="schema_debug.php">Open schema debug report</a>
<a class="btn btn-secondary" href="index.php">Retry dashboard</a>
</div>
<p class="note">A copy of this failure was written to <code>runtime_debug.log</code>. For private troubleshooting you can also add <code>?debug=1</code> to the URL to force expanded details.</p>
</section>
</main>
</body>
@ -544,6 +918,32 @@ if (!defined('APP_RUNTIME_DEBUG_HANDLER_REGISTERED')) {
});
}
if (!defined('APP_RUNTIME_DEBUG_FATAL_HANDLER_REGISTERED')) {
define('APP_RUNTIME_DEBUG_FATAL_HANDLER_REGISTERED', true);
register_shutdown_function(static function (): void {
if (!empty($GLOBALS['app_runtime_debug_rendered'])) {
return;
}
$error = error_get_last();
if (!runtime_debug_is_fatal_error($error)) {
return;
}
$throwable = new ErrorException(
(string)($error['message'] ?? 'Fatal error'),
0,
(int)($error['type'] ?? E_ERROR),
(string)($error['file'] ?? __FILE__),
(int)($error['line'] ?? 0)
);
runtime_debug_render_exception($throwable);
});
}
runtime_debug_mark('boot:runtime_debug_ready');
// Handle Outlet Switch
if (isset($_GET['action']) && $_GET['action'] === 'switch_outlet' && isset($_GET['id'])) {
$target_id = (int)$_GET['id'];
@ -579,23 +979,31 @@ try {
// Ignore if DB not ready
}
runtime_debug_mark('boot:loading_database_installer');
require_once 'includes/DatabaseInstaller.php';
runtime_debug_mark('boot:database_installer_loaded');
// Auto-install database if not installed, then ensure pending migrations are applied.
try {
if (!DatabaseInstaller::isInstalled()) {
runtime_debug_mark('boot:database_installing');
DatabaseInstaller::install();
} elseif (method_exists('DatabaseInstaller', 'ensureCurrentSchema')) {
runtime_debug_mark('boot:database_schema_sync');
DatabaseInstaller::ensureCurrentSchema();
} else {
error_log('Skipping DatabaseInstaller::ensureCurrentSchema() because the loaded DatabaseInstaller class does not define it.');
}
runtime_debug_mark('boot:database_ready');
} catch (Throwable $e) {
die("Installation Error: " . $e->getMessage());
runtime_debug_mark('boot:database_installer_failed');
runtime_debug_render_exception($e);
}
runtime_debug_mark('boot:loading_license_dependencies');
require_once 'lib/LicenseService.php';
require_once 'includes/lang.php';
runtime_debug_mark('boot:license_dependencies_loaded');
// Language Setup
if (isset($_GET['lang'])) {
@ -632,12 +1040,16 @@ try {
$is_activated = LicenseService::isActivated();
$trial_days = LicenseService::getTrialRemainingDays();
$can_access = LicenseService::canAccess();
runtime_debug_mark('boot:license_validated');
} catch (PDOException $e) {
die("Database Connection Error: " . $e->getMessage() . "<br><br>Please check your <b>db/config.php</b> settings.");
runtime_debug_mark('boot:license_validation_failed', ['reason' => 'pdo']);
runtime_debug_render_exception($e);
} catch (Exception $e) {
die("Application Error: " . $e->getMessage());
runtime_debug_mark('boot:license_validation_failed', ['reason' => 'application']);
runtime_debug_render_exception($e);
}
$page = $_GET['page'] ?? 'dashboard';
runtime_debug_mark('page:selected', ['page' => (string)$page, 'phase' => 'activation_gate']);
if (!$can_access && $page !== 'activate') {
header("Location: " . page_url("activate"));
@ -3357,7 +3769,7 @@ if (isset($_POST['add_hr_department'])) {
}
}
require 'pages/settings_save_logic.php';
runtime_debug_require('pages/settings_save_logic.php', ['phase' => 'save_logic', 'page' => (string)$page]);
// --- Backup Handlers ---
if (isset($_POST['create_backup'])) {
@ -3604,6 +4016,7 @@ if (isset($_POST['add_hr_department'])) {
// Routing & Data Fetching
$page = $_GET['page'] ?? 'dashboard';
runtime_debug_mark('page:selected', ['page' => (string)$page, 'phase' => 'data']);
// Permission map for pages
$page_permissions = [
@ -3962,6 +4375,8 @@ if ($page === 'export') {
exit;
}
runtime_debug_mark('page:shared_data_loading', ['page' => (string)$page]);
// Global data for modals
$current_oid = current_outlet_id();
$stmt = db()->prepare("SELECT * FROM stock_categories WHERE outlet_id = ? ORDER BY name_en ASC");
@ -3998,6 +4413,7 @@ $limit = isset($_GET["limit"]) ? max(5, (int)$_GET["limit"]) : 20;
$page_num = isset($_GET["p"]) ? (int)$_GET["p"] : 1;
if ($page_num < 1) $page_num = 1;
$offset = ($page_num - 1) * $limit;
runtime_debug_mark('page:data_switch_loading', ['page' => (string)$page]);
switch ($page) {
case 'suppliers':
$supplierTaxColumn = entity_tax_column('suppliers');
@ -4244,7 +4660,7 @@ switch ($page) {
$stmt->execute();
$data['outlets'] = $stmt->fetchAll();
break;
case 'copy_outlet_data': require 'pages/copy_outlet_data_logic.php'; break;
case 'copy_outlet_data': runtime_debug_require('pages/copy_outlet_data_logic.php', ['phase' => 'logic', 'page' => (string)$page]); break;
case 'settings':
// Already fetched globally
break;
@ -4255,7 +4671,7 @@ switch ($page) {
break;
case 'sales':
case 'purchases':
require 'pages/sales_purchases_logic.php';
runtime_debug_require('pages/sales_purchases_logic.php', ['phase' => 'logic', 'page' => (string)$page]);
break;
case 'sales_returns':
@ -4394,7 +4810,7 @@ switch ($page) {
$data['role_groups'] = db()->query("SELECT * FROM role_groups ORDER BY name ASC")->fetchAll();
break;
case 'users':
require 'pages/users_logic.php';
runtime_debug_require('pages/users_logic.php', ['phase' => 'logic', 'page' => (string)$page]);
break;
case 'backups':
$data['backups'] = BackupService::getBackups();
@ -4403,7 +4819,7 @@ switch ($page) {
$data['backup_settings'] = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
break;
case 'accounting':
require 'pages/accounting_logic.php';
runtime_debug_require('pages/accounting_logic.php', ['phase' => 'logic', 'page' => (string)$page]);
break;
case 'expense_report':
$start_date = $_GET['start_date'] ?? date('Y-m-01');
@ -4691,7 +5107,9 @@ switch ($page) {
break;
}
runtime_debug_mark('page:data_loaded', ['page' => (string)$page]);
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
runtime_debug_mark('page:rendering', ['page' => (string)$page]);
?>
<!doctype html>
<html lang="<?= $lang ?>" dir="<?= $dir ?>">
@ -8077,7 +8495,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</div>
<?php elseif ($page === 'sales' || $page === 'purchases'): ?>
<?php require 'pages/sales_purchases_view.php'; ?>
<?php runtime_debug_require('pages/sales_purchases_view.php', ['phase' => 'view', 'page' => (string)$page]); ?>
<?php elseif ($page === 'customer_statement' || $page === 'supplier_statement'): ?>
<div class="card p-4">
@ -8579,7 +8997,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</div>
<?php elseif ($page === 'accounting'): ?>
<?php require 'pages/accounting_view.php'; ?>
<?php runtime_debug_require('pages/accounting_view.php', ['phase' => 'view', 'page' => (string)$page]); ?>
<?php elseif ($page === 'expenses'): ?>
<?php $expenseCategories = $data['expense_categories'] ?? []; ?>
<div class="card p-4">
@ -9844,9 +10262,9 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<?php elseif ($page === "outlets" && ($_SESSION["user_role_name"] ?? "") === "Administrator"): ?>
<?php require "outlets_html.php"; ?>
<?php elseif ($page === 'copy_outlet_data'): require 'pages/copy_outlet_data_view.php'; ?>
<?php elseif ($page === 'settings'): require 'pages/settings_view.php'; ?>
<?php elseif ($page === 'role_groups'): require 'pages/role_groups_view.php'; ?>
<?php elseif ($page === 'copy_outlet_data'): runtime_debug_require('pages/copy_outlet_data_view.php', ['phase' => 'view', 'page' => (string)$page]); ?>
<?php elseif ($page === 'settings'): runtime_debug_require('pages/settings_view.php', ['phase' => 'view', 'page' => (string)$page]); ?>
<?php elseif ($page === 'role_groups'): runtime_debug_require('pages/role_groups_view.php', ['phase' => 'view', 'page' => (string)$page]); ?>
<?php elseif ($page === 'customer_display_settings'): ?>
<div class="card p-4">
@ -10003,7 +10421,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</div>
<?php elseif ($page === 'users'): ?>
<?php require 'pages/users_view.php'; ?>
<?php runtime_debug_require('pages/users_view.php', ['phase' => 'view', 'page' => (string)$page]); ?>
<?php elseif ($page === 'cash_registers'): ?>
<div class="card p-4 rounded-4 shadow-sm border-0">
<div class="d-flex justify-content-between align-items-center mb-4">
@ -10294,7 +10712,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</div>
</div>
<?php require 'pages/register_session_report_script.php'; ?>
<?php runtime_debug_require('pages/register_session_report_script.php', ['phase' => 'script', 'page' => (string)$page]); ?>
<!-- Open Register Modal -->
<div class="modal fade" id="openRegisterModal" tabindex="-1">
@ -10464,7 +10882,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<?php endif; ?>
<?php elseif ($page === 'logs'): ?>
<?php require 'pages/logs_view.php'; ?>
<?php runtime_debug_require('pages/logs_view.php', ['phase' => 'view', 'page' => (string)$page]); ?>
<?php endif; ?>
</div>
@ -11108,17 +11526,17 @@ document.addEventListener('DOMContentLoaded', function() {
});
});
<?php require 'pages/lpo_quotation_script.php'; ?>
<?php runtime_debug_require('pages/lpo_quotation_script.php', ['phase' => 'script', 'page' => (string)$page]); ?>
<?php if ($page === 'sales' || $page === 'purchases'): ?>
<?php require 'pages/sales_purchases_page_script.php'; ?>
<?php runtime_debug_require('pages/sales_purchases_page_script.php', ['phase' => 'script', 'page' => (string)$page]); ?>
<?php endif; ?>
} catch (e) { console.error("JS Error in DOMContentLoaded:", e); }
});
</script>
<?php if ($page === 'sales' || $page === 'purchases'): ?>
<?php require 'pages/sales_purchases_modals.php'; ?>
<?php runtime_debug_require('pages/sales_purchases_modals.php', ['phase' => 'view', 'page' => (string)$page]); ?>
<?php endif; ?>
<style>
@ -11457,12 +11875,12 @@ document.addEventListener('DOMContentLoaded', function() {
}
</style>
<?php require 'pages/avery_label_script.php'; ?>
<?php runtime_debug_require('pages/avery_label_script.php', ['phase' => 'script', 'page' => (string)$page]); ?>
<script>
<?php require 'pages/barcode_pos_script.php'; ?>
<?php runtime_debug_require('pages/barcode_pos_script.php', ['phase' => 'script', 'page' => (string)$page]); ?>
<?php require 'pages/language_dashboard_script.php'; ?>
<?php runtime_debug_require('pages/language_dashboard_script.php', ['phase' => 'script', 'page' => (string)$page]); ?>
</script>
</body>