4.6 KiB
4.6 KiB
PLAN - Overtime Summary Mapping Fix
This document outlines the plan to resolve the issue where Overtime Hours and Overtime Earnings are missing (showing as 0) in the Payroll Run Summary table, despite being correctly computed and displayed on individual employee payslips.
1. Problem Analysis & Scope
Current Behavior
- When a payroll run is processed,
PayrollRun::processEmployeePayrollcreates aPayrollEntryrow. - The mapping of overtime data is currently hardcoded or incorrect in PayrollRun.php:
'overtime_hours' => 0, 'overtime_amount' => $summary['reg_ot'] ?? 0, - Because
$summaryfromPayrollServicereturnsreg_ot_hoursandreg_ot_amountinstead ofreg_ot, both fields are stored as0in the database. - The frontend show.tsx reads directly from these columns, resulting in
0display in the summary grid. - The payslip template template.blade.php is unaffected because it reads directly from the nested
earnings_breakdownJSON structure.
Goals
- Update
PayrollRunmapping to populateovertime_hoursandovertime_amountcolumns. - Update the
SimulatePayslipsartisan command to also populate these columns when running simulations. - Write a database migration to backfill historical
payroll_entriesrecords by parsing theirearnings_breakdownJSON columns, ensuring previously generated payrolls display overtime correctly.
2. Proposed Changes
A. Code Changes
1. app/Models/PayrollRun.php
Map the correct overtime summary keys from PayrollService:
- 'overtime_hours' => 0,
- 'overtime_amount' => $summary['reg_ot'] ?? 0,
+ 'overtime_hours' => (float) ($summary['reg_ot_hours'] ?? 0),
+ 'overtime_amount' => (float) ($summary['reg_ot_amount'] ?? 0),
2. app/Console/Commands/SimulatePayslips.php
Ensure simulated payroll entries populate overtime columns:
$payrollEntry = PayrollEntry::create([
'payroll_run_id' => $payrollRun->id,
'employee_id' => $user->id,
'basic_salary' => $computation['basic_salary'],
'gross_pay' => $computation['total_earnings'],
'total_earnings' => $computation['total_earnings'],
'total_deductions' => $computation['total_deductions'],
'net_pay' => $computation['net_pay'],
'earnings_breakdown' => $computation['earnings'],
'deductions_breakdown' => $computation['deductions'],
+ 'overtime_hours' => (float) ($computation['summary']['reg_ot_hours'] ?? 0),
+ 'overtime_amount' => (float) ($computation['summary']['reg_ot_amount'] ?? 0),
'created_by' => 1,
]);
B. Database Migration (Backfill Historical Data)
Create a new migration database/migrations/2026_06_26_150000_backfill_payroll_entry_overtime.php that fetches all existing payroll_entries, parses the earnings_breakdown JSON, and updates the overtime_hours and overtime_amount fields:
public function up()
{
$entries = \DB::table('payroll_entries')->get();
foreach ($entries as $entry) {
$breakdown = json_decode($entry->earnings_breakdown, true);
$summary = $breakdown['summary'] ?? [];
$otHours = (float) ($summary['reg_ot_hours'] ?? 0);
$otAmount = (float) ($summary['reg_ot_amount'] ?? 0);
if ($otHours > 0 || $otAmount > 0) {
\DB::table('payroll_entries')
->where('id', $entry->id)
->update([
'overtime_hours' => $otHours,
'overtime_amount' => $otAmount,
]);
}
}
}
3. Verification & Testing Checklist
- Database Migration Execution: Run
php artisan migratelocally and verify the migration runs without error. - Historical Data Backfill: Query the
payroll_entriestable before and after migration to verifyovertime_hoursandovertime_amountare populated for older entries. - New Payroll Run Processing: Create a new payroll run, verify that the database fields are correctly calculated and stored.
- Frontend View: Verify the Payroll Run Summary page displays the correct non-zero overtime columns.
- Payslip Preservation: Confirm that payslip download and preview layouts remain correct and unaffected.