|
Server : LiteSpeed System : Linux terra.hostitbro.com 5.14.0-611.54.3.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 7 16:31:24 EDT 2026 x86_64 User : outerorb ( 1091) PHP Version : 8.1.34 Disable Function : mail Directory : /home2/outerorb/emp.outerorbittech.in/admin/ | |
|
Path: /home2/outerorb/emp.outerorbittech.in/admin/salary.php
Size: 17.17 KB
Permissions: 0666
<?php
require __DIR__ . '/../includes/helpers.php';
require_admin();
ensure_payroll_tables();
ensure_employees_table();
$roleLabel = 'Admin';
$pdo = db();
$flash = flash();
$admin = current_admin();
$errors = [];
$empId = isset($_GET['emp']) ? (int)$_GET['emp'] : 0;
// POST: save/update salary structure
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!verify_csrf($_POST['csrf_token'] ?? '')) {
redirect_with_message('salary.php', 'Session expired.', 'error');
}
$action = $_POST['action'] ?? '';
$eid = (int)($_POST['employee_id'] ?? 0);
if ($action === 'save' && $eid) {
$fields = ['basic','hra','da','conveyance','special_allowance','other_allowance'];
$vals = [];
foreach ($fields as $f) { $vals[$f] = max(0, (float)str_replace(',','',$_POST[$f] ?? '0')); }
$effFrom = sanitize_text($_POST['effective_from'] ?? date('Y-m-01'));
$pfA = isset($_POST['pf_applicable']) ? 1 : 0;
$esiA = isset($_POST['esi_applicable']) ? 1 : 0;
$ptA = isset($_POST['pt_applicable']) ? 1 : 0;
// Archive to revisions first
$existing = get_salary_structure($eid);
if ($existing) {
$pdo->prepare(
'INSERT INTO salary_revisions (employee_id,basic,hra,da,conveyance,special_allowance,other_allowance,effective_from,created_by)
VALUES (?,?,?,?,?,?,?,?,?)'
)->execute([$eid, $existing['basic'], $existing['hra'], $existing['da'],
$existing['conveyance'], $existing['special_allowance'], $existing['other_allowance'],
$existing['effective_from'], $admin['id'] ?? null]);
}
$pdo->prepare(
'INSERT INTO salary_structures (employee_id,basic,hra,da,conveyance,special_allowance,other_allowance,pf_applicable,esi_applicable,pt_applicable,effective_from)
VALUES (?,?,?,?,?,?,?,?,?,?,?)
ON DUPLICATE KEY UPDATE basic=VALUES(basic),hra=VALUES(hra),da=VALUES(da),
conveyance=VALUES(conveyance),special_allowance=VALUES(special_allowance),
other_allowance=VALUES(other_allowance),pf_applicable=VALUES(pf_applicable),
esi_applicable=VALUES(esi_applicable),pt_applicable=VALUES(pt_applicable),
effective_from=VALUES(effective_from),updated_at=NOW()'
)->execute([$eid, $vals['basic'], $vals['hra'], $vals['da'],
$vals['conveyance'], $vals['special_allowance'], $vals['other_allowance'],
$pfA, $esiA, $ptA, $effFrom]);
redirect_with_message('salary.php?emp='.$eid, 'Salary structure saved.');
}
}
// Load selected employee
$selectedEmp = null;
$ss = null;
$revisions = [];
if ($empId) {
$stmt = $pdo->prepare('SELECT * FROM employees WHERE id = ? AND deleted_at IS NULL LIMIT 1');
$stmt->execute([$empId]);
$selectedEmp = $stmt->fetch() ?: null;
if ($selectedEmp) {
$ss = get_salary_structure($empId);
$rev = $pdo->prepare('SELECT * FROM salary_revisions WHERE employee_id = ? ORDER BY effective_from DESC LIMIT 10');
$rev->execute([$empId]);
$revisions = $rev->fetchAll();
}
}
// Employee list
$where = ['e.deleted_at IS NULL', "e.employee_status = 'active'"];
$params = [];
apply_department_filter($where, $params);
$empList = $pdo->prepare('SELECT e.id, e.first_name, e.last_name, e.department,
(SELECT basic FROM salary_structures WHERE employee_id = e.id) AS basic
FROM employees e WHERE ' . implode(' AND ', $where) . ' ORDER BY e.first_name');
$empList->execute($params);
$empList = $empList->fetchAll();
function e(string $v): string { return htmlspecialchars($v, ENT_QUOTES, 'UTF-8'); }
function fmt(float $v): string { return number_format($v, 2); }
$grossPreview = 0;
if ($ss) {
$grossPreview = $ss['basic'] + $ss['hra'] + $ss['da'] + $ss['conveyance'] + $ss['special_allowance'] + $ss['other_allowance'];
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Salary Structures — HRMS</title>
<link rel="stylesheet" href="../assets/css/style.css?v=<?php echo filemtime(__DIR__ . '/../assets/css/style.css'); ?>" />
<link rel="stylesheet" href="../assets/css/polish.css?v=<?php echo filemtime(__DIR__ . '/../assets/css/polish.css'); ?>" />
<style>
.two-col { display:grid; grid-template-columns:280px 1fr; gap:20px; }
.emp-list-item { padding:10px 12px; border-bottom:1px solid #f3f4f6; cursor:pointer; display:flex; justify-content:space-between; align-items:center; }
.emp-list-item:hover { background:#f9fafb; }
.emp-list-item.active { background:#eff6ff; font-weight:700; }
.sal-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:14px; }
.gross-preview { background:#f0fdf4; border:1px solid #bbf7d0; border-radius:8px; padding:16px; margin-top:16px; }
</style>
</head>
<body class="dashboard-layout">
<div class="app-wrapper">
<?php require '_sidebar.php'; ?>
<div class="main-content">
<div class="top-bar">
<div class="top-bar-left">
<button class="sidebar-toggle" id="sidebarToggle" aria-label="Toggle sidebar">☰</button>
<div>
<h1 class="page-title">Salary Management</h1>
<div class="breadcrumb-nav">
<span>Add and manage employee salaries</span>
</div>
</div>
</div>
<div class="top-bar-right">
<a href="dashboard.php" style="color: #cbd5e1; text-decoration: none; padding: 8px 12px; border-radius: 6px; transition: background 0.15s; font-size: 13px; font-weight: 600;">← Dashboard</a>
</div>
</div>
<div class="dashboard-container" style="max-width: 1200px;">
<div class="container">
<div class="header">
<div class="brand">
<div class="brand-title">
<h1 style="margin:0;">Salary Structures</h1>
<p class="helper" style="margin:2px 0 0;">Define CTC components per employee.</p>
</div>
</div>
<div class="actions">
<a class="badge" href="payroll.php">Run Payroll</a>
<a class="badge" href="dashboard.php">Dashboard</a>
<a class="badge" href="logout.php">Logout</a>
</div>
</div>
<?php if ($flash): ?>
<div class="alert <?php echo $flash['type'] === 'error' ? 'alert-error' : 'alert-success'; ?>">
<?php echo e($flash['message']); ?>
</div>
<?php endif; ?>
<!-- Employee list as table -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:14px; background:#f9fafb; border-bottom:1px solid #e5e7eb; font-weight:700; font-size:.9rem;">
Employees (<?php echo count($empList); ?>)
</div>
<div style="overflow-x:auto;">
<table style="width:100%; border-collapse:collapse;">
<thead>
<tr style="background:#f9fafb;">
<th style="padding:10px 14px; border-bottom:1px solid #e5e7eb; text-align:left; font-size:.85rem; font-weight:600; color:#6b7280;">Name</th>
<th style="padding:10px 14px; border-bottom:1px solid #e5e7eb; text-align:left; font-size:.85rem; font-weight:600; color:#6b7280;">Department</th>
<th style="padding:10px 14px; border-bottom:1px solid #e5e7eb; text-align:right; font-size:.85rem; font-weight:600; color:#6b7280;">Basic Salary</th>
<th style="padding:10px 14px; border-bottom:1px solid #e5e7eb; text-align:center; font-size:.85rem; font-weight:600; color:#6b7280;">Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($empList as $emp): ?>
<tr style="border-bottom:1px solid #e5e7eb; hover-effect:true;" onmouseover="this.style.background='#f9fafb';" onmouseout="this.style.background='#fff';">
<td style="padding:10px 14px; font-size:.875rem;"><?php echo e($emp['first_name'].' '.$emp['last_name']); ?></td>
<td style="padding:10px 14px; font-size:.85rem; color:#6b7280;"><?php echo e($emp['department']); ?></td>
<td style="padding:10px 14px; font-size:.875rem; text-align:right; color:<?php echo $emp['basic'] ? '#16a34a' : '#d97706'; ?>; font-weight:500;">
<?php echo $emp['basic'] ? '₹'.number_format($emp['basic']) : 'Not set'; ?>
</td>
<td style="padding:10px 14px; text-align:center;">
<a href="salary.php?emp=<?php echo (int)$emp['id']; ?>" class="btn btn-sm btn-primary" style="font-size:.8rem; padding:6px 10px;">
<?php echo $empId === (int)$emp['id'] ? '<i class="fas fa-check-circle" style="color:#10b981;"></i> Selected' : 'Select'; ?>
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<div class="two-col" style="grid-template-columns: 1fr;">
<!-- Right panel -->
<div>
<!-- Right panel -->
<div>
<?php if (!$selectedEmp): ?>
<div class="card" style="color:#6b7280; text-align:center; padding:60px;">
Select an employee from the list to view or set their salary structure.
</div>
<?php else: ?>
<div class="card" style="margin-bottom:16px;">
<h3 style="margin:0 0 4px;"><?php echo e($selectedEmp['first_name'].' '.$selectedEmp['last_name']); ?></h3>
<p style="margin:0 0 20px; color:#6b7280; font-size:.875rem;"><?php echo e($selectedEmp['department']); ?></p>
<form method="POST">
<input type="hidden" name="csrf_token" value="<?php echo e(csrf_token()); ?>" />
<input type="hidden" name="action" value="save" />
<input type="hidden" name="employee_id" value="<?php echo (int)$empId; ?>" />
<div class="sal-grid">
<?php
$salFields = [
'basic' => 'Basic Salary',
'hra' => 'HRA',
'da' => 'DA',
'conveyance' => 'Conveyance',
'special_allowance' => 'Special Allowance',
'other_allowance' => 'Other Allowance',
];
foreach ($salFields as $field => $label): ?>
<div class="form-group" style="margin:0;">
<label><?php echo $label; ?> (₹/mo)</label>
<input type="number" name="<?php echo $field; ?>" class="form-control" min="0" step="0.01"
value="<?php echo $ss ? number_format((float)$ss[$field], 2, '.', '') : '0'; ?>"
oninput="updateGross()" />
</div>
<?php endforeach; ?>
</div>
<div style="display:flex; gap:20px; margin-top:16px; flex-wrap:wrap;">
<label style="display:flex; align-items:center; gap:6px; font-size:.875rem;">
<input type="checkbox" name="pf_applicable" <?php echo (!$ss || $ss['pf_applicable']) ? 'checked' : ''; ?> /> PF (12% Basic)
</label>
<label style="display:flex; align-items:center; gap:6px; font-size:.875rem;">
<input type="checkbox" name="esi_applicable" <?php echo (!$ss || $ss['esi_applicable']) ? 'checked' : ''; ?> /> ESI (0.75% Gross)
</label>
<label style="display:flex; align-items:center; gap:6px; font-size:.875rem;">
<input type="checkbox" name="pt_applicable" <?php echo (!$ss || $ss['pt_applicable']) ? 'checked' : ''; ?> /> Professional Tax
</label>
</div>
<div class="form-group" style="margin-top:14px; max-width:200px;">
<label>Effective From</label>
<input type="date" name="effective_from" class="form-control"
value="<?php echo e($ss ? $ss['effective_from'] : date('Y-m-01')); ?>" />
</div>
<div class="gross-preview">
<strong>Monthly Gross: </strong>
<span id="gross-preview">₹<?php echo fmt($grossPreview); ?></span>
|
<strong>Annual CTC: </strong>
<span id="ctc-preview">₹<?php echo fmt($grossPreview * 12); ?></span>
</div>
<button type="submit" class="btn btn-primary" style="margin-top:16px;">Save Structure</button>
</form>
</div>
<?php if (!empty($revisions)): ?>
<div class="card">
<h4 style="margin:0 0 12px; font-size:.875rem; text-transform:uppercase; letter-spacing:.04em; color:#6b7280;">Revision History</h4>
<table style="width:100%; border-collapse:collapse; font-size:.82rem;">
<thead>
<tr style="background:#f9fafb;">
<?php foreach (['Effective','Basic','HRA','DA','Conv.','Special','Other','Gross'] as $h): ?>
<th style="padding:7px 10px; border-bottom:1px solid #e5e7eb; text-align:right; white-space:nowrap;"><?php echo $h; ?></th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<?php foreach ($revisions as $r):
$rGross = $r['basic']+$r['hra']+$r['da']+$r['conveyance']+$r['special_allowance']+$r['other_allowance'];
?>
<tr>
<td style="padding:6px 10px; border-bottom:1px solid #f3f4f6;"><?php echo date('d M Y', strtotime($r['effective_from'])); ?></td>
<td style="padding:6px 10px; border-bottom:1px solid #f3f4f6; text-align:right;">₹<?php echo fmt($r['basic']); ?></td>
<td style="padding:6px 10px; border-bottom:1px solid #f3f4f6; text-align:right;">₹<?php echo fmt($r['hra']); ?></td>
<td style="padding:6px 10px; border-bottom:1px solid #f3f4f6; text-align:right;">₹<?php echo fmt($r['da']); ?></td>
<td style="padding:6px 10px; border-bottom:1px solid #f3f4f6; text-align:right;">₹<?php echo fmt($r['conveyance']); ?></td>
<td style="padding:6px 10px; border-bottom:1px solid #f3f4f6; text-align:right;">₹<?php echo fmt($r['special_allowance']); ?></td>
<td style="padding:6px 10px; border-bottom:1px solid #f3f4f6; text-align:right;">₹<?php echo fmt($r['other_allowance']); ?></td>
<td style="padding:6px 10px; border-bottom:1px solid #f3f4f6; text-align:right; font-weight:700;">₹<?php echo fmt($rGross); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sidebar-overlay" id="sidebarOverlay"></div>
<script>
const sidebar = document.getElementById('sidebar');
const sidebarToggle = document.getElementById('sidebarToggle');
const sidebarOverlay = document.getElementById('sidebarOverlay');
sidebarToggle?.addEventListener('click', () => {
sidebar.classList.toggle('open');
sidebarOverlay.classList.toggle('open');
});
sidebarOverlay?.addEventListener('click', () => {
sidebar.classList.remove('open');
sidebarOverlay.classList.remove('open');
});
</script>
<script>
function updateGross() {
const fields = ['basic','hra','da','conveyance','special_allowance','other_allowance'];
let gross = 0;
fields.forEach(f => {
const el = document.querySelector('[name="' + f + '"]');
if (el) gross += parseFloat(el.value) || 0;
});
document.getElementById('gross-preview').textContent = '₹' + gross.toLocaleString('en-IN', {minimumFractionDigits:2});
document.getElementById('ctc-preview').textContent = '₹' + (gross*12).toLocaleString('en-IN', {minimumFractionDigits:2});
}
</script>
</body>
</html>