Next is **Overtime Approval Module**.

This should sit between Attendance and Payroll:

```text
Attendance
    ↓
Overtime Request
    ↓
Manager Approval
    ↓
HR Approval
    ↓
Payroll Calculation
    ↓
Payslip
```

This prevents staff from automatically getting paid for every extra hour and lets management approve overtime first.

# SQL 1 — Overtime Requests

```sql
CREATE TABLE overtime_requests (

    id INT AUTO_INCREMENT PRIMARY KEY,

    employee_id INT NOT NULL,

    attendance_id INT NULL,

    overtime_date DATE NOT NULL,

    overtime_hours DECIMAL(10,2) NOT NULL,

    reason TEXT,

    manager_status ENUM(
        'Pending',
        'Approved',
        'Rejected'
    ) DEFAULT 'Pending',

    hr_status ENUM(
        'Pending',
        'Approved',
        'Rejected'
    ) DEFAULT 'Pending',

    created_at TIMESTAMP
    DEFAULT CURRENT_TIMESTAMP
);
```

---

# Directory

```text
overtime_management/

├── overtime_request.php
├── overtime_request_script.php

├── overtime_approval.php
├── overtime_approval_script.php

├── overtime_reports.php
├── overtime_reports_script.php
```

---

# overtime_request_script.php

```php
<?php

error_reporting(E_ALL);
ini_set('display_errors',1);

require '../config.php';
session_start();

$userId = intval($_SESSION['user_id']);

if($_SERVER['REQUEST_METHOD']=='POST'){

    $date = $_POST['overtime_date'];

    $hours =
    floatval($_POST['overtime_hours']);

    $reason =
    trim($_POST['reason']);

    $stmt = $conn->prepare("
        INSERT INTO overtime_requests
        (
            employee_id,
            overtime_date,
            overtime_hours,
            reason
        )
        VALUES
        (?,?,?,?)
    ");

    $stmt->bind_param(
        "isds",
        $userId,
        $date,
        $hours,
        $reason
    );

    $stmt->execute();

    header(
        "Location:overtime_request.php?saved=1"
    );

    exit;
}
```

---

# overtime_request.php

```php
<?php
require 'overtime_request_script.php';
?>

<!DOCTYPE html>
<html>
<head>

<title>Overtime Request</title>

<script src="https://cdn.tailwindcss.com"></script>

</head>

<body class="flex h-screen bg-gray-100">

<?php include '../sidebar.php'; ?>

<div class="flex-1 p-8">

<h1 class="text-3xl font-bold mb-6">

Overtime Request

</h1>

<?php if(isset($_GET['saved'])): ?>

<div class="bg-green-100 p-4 rounded-lg mb-4">

Request submitted successfully.

</div>

<?php endif; ?>

<div class="bg-white p-6 rounded-xl shadow">

<form method="POST">

<div class="grid md:grid-cols-2 gap-5">

<div>

<label class="block mb-2">

Date

</label>

<input
type="date"
name="overtime_date"
required
class="w-full border p-3 rounded-lg">

</div>

<div>

<label class="block mb-2">

Hours

</label>

<input
type="number"
step="0.25"
name="overtime_hours"
required
class="w-full border p-3 rounded-lg">

</div>

</div>

<div class="mt-5">

<label class="block mb-2">

Reason

</label>

<textarea
name="reason"
rows="4"
required
class="w-full border p-3 rounded-lg"></textarea>

</div>

<button
class="mt-5 bg-blue-600 text-white px-8 py-3 rounded-lg">

Submit Request

</button>

</form>

</div>

</div>

</body>
</html>
```

---

# overtime_approval_script.php

Supports:

```text
Manager Approval
HR Approval
```

```php
<?php

error_reporting(E_ALL);
ini_set('display_errors',1);

require '../config.php';
session_start();

$userRole =
strtolower($_SESSION['role']);

if(
    isset($_GET['approve'])
){

    $id =
    intval($_GET['approve']);

    if($userRole=='manager'){

        $conn->query("
        UPDATE overtime_requests
        SET manager_status='Approved'
        WHERE id=$id
        ");

    }

    if(
        in_array(
            $userRole,
            ['hr','admin','superadmin']
        )
    ){

        $conn->query("
        UPDATE overtime_requests
        SET hr_status='Approved'
        WHERE id=$id
        ");
    }

    header(
        "Location:overtime_approval.php"
    );
    exit;
}

$res = $conn->query("
SELECT

o.*,
e.name employee_name,
d.name department

FROM overtime_requests o

LEFT JOIN employees e
ON e.id=o.employee_id

LEFT JOIN departments d
ON d.id=e.department_id

ORDER BY o.created_at DESC
");

$requests=[];

while($row=$res->fetch_assoc()){

    $requests[]=$row;
}
```

---

# overtime_approval.php

```php
<?php
require 'overtime_approval_script.php';
?>

<!DOCTYPE html>
<html>
<head>

<title>Overtime Approval</title>

<script src="https://cdn.tailwindcss.com"></script>

</head>

<body class="flex h-screen bg-gray-100">

<?php include '../sidebar.php'; ?>

<div class="flex-1 p-8">

<h1 class="text-3xl font-bold mb-6">

Overtime Approval

</h1>

<div class="bg-white rounded-xl shadow overflow-hidden">

<table class="w-full">

<thead class="bg-gray-100">

<tr>

<th class="p-4 text-left">
Employee
</th>

<th class="p-4">
Department
</th>

<th class="p-4">
Date
</th>

<th class="p-4">
Hours
</th>

<th class="p-4">
Manager
</th>

<th class="p-4">
HR
</th>

<th class="p-4">
Action
</th>

</tr>

</thead>

<tbody>

<?php foreach($requests as $row): ?>

<tr class="border-b">

<td class="p-4">

<?= htmlspecialchars(
$row['employee_name']
) ?>

</td>

<td class="p-4">

<?= htmlspecialchars(
$row['department']
) ?>

</td>

<td class="p-4">

<?= $row['overtime_date'] ?>

</td>

<td class="p-4 text-center">

<?= $row['overtime_hours'] ?>

</td>

<td class="p-4 text-center">

<?= $row['manager_status'] ?>

</td>

<td class="p-4 text-center">

<?= $row['hr_status'] ?>

</td>

<td class="p-4 text-center">

<a
href="?approve=<?= $row['id'] ?>"
class="bg-green-600 text-white px-3 py-2 rounded">

Approve

</a>

</td>

</tr>

<?php endforeach; ?>

</tbody>

</table>

</div>

</div>

</body>
</html>
```

---

# Payroll Integration

Inside your `payroll_script.php`

Replace the overtime section with:

```php
$overtimeHours = 0;

$overtimeQuery = $conn->query("
SELECT
SUM(overtime_hours) total
FROM overtime_requests

WHERE employee_id = $empId

AND manager_status='Approved'

AND hr_status='Approved'

AND MONTH(overtime_date)=$month

AND YEAR(overtime_date)=$year
");

if($row=$overtimeQuery->fetch_assoc()){

    $overtimeHours =
    floatval($row['total']);
}

$overtimePay =
$overtimeHours *
floatval($emp['overtime_rate']);
```

Then:

```php
$gross += $overtimePay;
```

---

# Add to Payslip

Add rows:

```php
<tr>
<td>Overtime Hours</td>
<td><?= number_format(
$payroll['overtime_hours'],
2
) ?></td>
</tr>

<tr>
<td>Overtime Pay</td>
<td>
₦<?= number_format(
$payroll['overtime_pay'],
2
) ?>
</td>
</tr>
```

---

After Overtime Approval, your HR suite becomes:

```text
✓ Attendance
✓ Shift Scheduling
✓ Leave Management
✓ Payroll
✓ Payslips
✓ Salary Settings
✓ Loan Management
✓ Salary Advance
✓ Overtime Approval

NEXT:
✓ Public Holidays
✓ Asset Assignment
✓ Performance Appraisal
✓ Employee Self Service Portal
✓ Payroll Approval Workflow
```

The next module after this should be **Public Holidays**, because it affects attendance, leave balances, overtime calculations, and payroll.
