181 lines
9.1 KiB
PHP
181 lines
9.1 KiB
PHP
<?php
|
|
session_start();
|
|
require_once __DIR__ . '/db/config.php';
|
|
|
|
if (!isset($_SESSION['user_id']) || !isset($_SESSION['company_id']) || $_SESSION['role'] !== 'admin') {
|
|
// Redirect non-admins to the dashboard or a generic error page
|
|
header('Location: /dashboard.php');
|
|
exit;
|
|
}
|
|
|
|
$company_id = $_SESSION['company_id'];
|
|
$error_message = '';
|
|
$success_message = '';
|
|
|
|
// Define the settings keys we are managing
|
|
$setting_keys = ['napsa_rate', 'napsa_ceiling', 'nhima_rate', 'paye_brackets'];
|
|
|
|
// Handle Settings Update
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_settings'])) {
|
|
try {
|
|
$pdo = db();
|
|
$pdo->beginTransaction();
|
|
|
|
foreach ($setting_keys as $key) {
|
|
if (isset($_POST[$key])) {
|
|
$value = $_POST[$key];
|
|
// For PAYE brackets, we expect a JSON string, so we don't validate it beyond checking if it's set
|
|
|
|
$stmt = $pdo->prepare(
|
|
"INSERT INTO settings (company_id, setting_key, setting_value) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)"
|
|
);
|
|
$stmt->execute([$company_id, $key, $value]);
|
|
}
|
|
}
|
|
|
|
$pdo->commit();
|
|
$success_message = 'Settings updated successfully!';
|
|
} catch (Exception $e) {
|
|
if ($pdo->inTransaction()) {
|
|
$pdo->rollBack();
|
|
}
|
|
$error_message = "Failed to update settings: " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
// Fetch current settings for the company
|
|
$settings = [];
|
|
$stmt = db()->prepare("SELECT setting_key, setting_value FROM settings WHERE company_id = ? AND setting_key IN ('" . implode("',\'", $setting_keys) . "')");
|
|
$stmt->execute([$company_id]);
|
|
$results = $stmt->fetchAll();
|
|
foreach ($results as $row) {
|
|
$settings[$row['setting_key']] = $row['setting_value'];
|
|
}
|
|
|
|
// Set default values if not present
|
|
$defaults = [
|
|
'napsa_rate' => '5', // 5%
|
|
'napsa_ceiling' => '2880', // ZMW 2880
|
|
'nhima_rate' => '1', // 1%
|
|
'paye_brackets' => json_encode([
|
|
['from' => 0, 'to' => 4800, 'rate' => 0],
|
|
['from' => 4800.01, 'to' => 6800, 'rate' => 20],
|
|
['from' => 6800.01, 'to' => 8900, 'rate' => 30],
|
|
['from' => 8900.01, 'to' => null, 'rate' => 37.5]
|
|
], JSON_PRETTY_PRINT)
|
|
];
|
|
|
|
foreach ($defaults as $key => $value) {
|
|
if (!isset($settings[$key])) {
|
|
$settings[$key] = $value;
|
|
}
|
|
}
|
|
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Payroll Settings - GPTPayroll</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
</head>
|
|
<body class="bg-gray-100 font-sans leading-normal tracking-normal">
|
|
<div class="flex md:flex-row-reverse flex-wrap">
|
|
|
|
<!-- Main Content -->
|
|
<div class="w-full md:w-4/5 bg-gray-100">
|
|
<div class="container bg-gray-100 pt-16 px-6 mx-auto">
|
|
|
|
<?php if ($error_message): ?>
|
|
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4" role="alert">
|
|
<span class="block sm:inline"><?= htmlspecialchars($error_message) ?></span>
|
|
</div>
|
|
<?php endif; ?>
|
|
<?php if ($success_message): ?>
|
|
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4" role="alert">
|
|
<span class="block sm:inline"><?= htmlspecialchars($success_message) ?></span>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="bg-white shadow-md rounded p-8">
|
|
<h1 class="text-2xl font-bold text-gray-800 mb-6">Payroll Statutory Settings</h1>
|
|
<p class="text-gray-600 mb-6">Configure the statutory rates for your company according to Zambian regulations.</p>
|
|
|
|
<form action="/settings.php" method="POST">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
|
<!-- NAPSA Settings -->
|
|
<div class="border p-6 rounded-lg">
|
|
<h3 class="text-lg font-semibold mb-4">NAPSA Settings</h3>
|
|
<div class="mb-4">
|
|
<label for="napsa_rate" class="block text-gray-700 text-sm font-bold mb-2">Contribution Rate (%)</label>
|
|
<input type="number" step="0.01" name="napsa_rate" id="napsa_rate" value="<?= htmlspecialchars($settings['napsa_rate']) ?>" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700">
|
|
<p class="text-xs text-gray-500 mt-1">Employee and employer each contribute this percentage. Default is 5%.</p>
|
|
</div>
|
|
<div class="mb-4">
|
|
<label for="napsa_ceiling" class="block text-gray-700 text-sm font-bold mb-2">Maximum Assessable Earnings (ZMW)</label>
|
|
<input type="number" step="0.01" name="napsa_ceiling" id="napsa_ceiling" value="<?= htmlspecialchars($settings['napsa_ceiling']) ?>" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700">
|
|
<p class="text-xs text-gray-500 mt-1">The maximum monthly salary amount on which NAPSA contributions are calculated.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- NHIMA Settings -->
|
|
<div class="border p-6 rounded-lg">
|
|
<h3 class="text-lg font-semibold mb-4">NHIMA Settings</h3>
|
|
<div class="mb-4">
|
|
<label for="nhima_rate" class="block text-gray-700 text-sm font-bold mb-2">Contribution Rate (%)</label>
|
|
<input type="number" step="0.01" name="nhima_rate" id="nhima_rate" value="<?= htmlspecialchars($settings['nhima_rate']) ?>" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700">
|
|
<p class="text-xs text-gray-500 mt-1">Employee and employer each contribute this percentage of the basic salary. Default is 1%.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- PAYE Brackets -->
|
|
<div class="mt-8 border p-6 rounded-lg">
|
|
<h3 class="text-lg font-semibold mb-4">PAYE Tax Brackets (Monthly)</h3>
|
|
<div class="mb-4">
|
|
<label for="paye_brackets" class="block text-gray-700 text-sm font-bold mb-2">Brackets (JSON format)</label>
|
|
<textarea name="paye_brackets" id="paye_brackets" rows="10" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 font-mono text-sm"><?= htmlspecialchars($settings['paye_brackets']) ?></textarea>
|
|
<p class="text-xs text-gray-500 mt-1">Use `null` for the upper limit of the last bracket. Rates are percentages.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-8">
|
|
<button type="submit" name="update_settings" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
|
|
Save Settings
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="w-full md:w-1/5 bg-gray-800 md:min-h-screen">
|
|
<div class="md:relative mx-auto lg:float-right lg:px-6">
|
|
<ul class="list-reset flex flex-row md:flex-col text-center md:text-left">
|
|
<li class="mr-3 flex-1">
|
|
<a href="/dashboard.php" class="block py-4 px-4 text-gray-400 hover:text-white no-underline">Dashboard</a>
|
|
</li>
|
|
<li class="mr-3 flex-1">
|
|
<a href="/employees.php" class="block py-4 px-4 text-gray-400 hover:text-white no-underline">Employees</a>
|
|
</li>
|
|
<li class="mr-3 flex-1">
|
|
<a href="/payroll.php" class="block py-4 px-4 text-gray-400 hover:text-white no-underline">Payroll</a>
|
|
</li>
|
|
<li class="mr-3 flex-1">
|
|
<a href="/payroll_history.php" class="block py-4 px-4 text-gray-400 hover:text-white no-underline">Payroll History</a>
|
|
</li>
|
|
<li class="mr-3 flex-1">
|
|
<a href="/settings.php" class="block py-4 px-4 text-white font-bold no-underline">Settings</a>
|
|
</li>
|
|
<li class="mr-3 flex-1">
|
|
<a href="/logout.php" class="block py-4 px-4 text-gray-400 hover:text-white no-underline">Logout</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|