Fix: Make payslip PDF template dynamically render all earnings and deductions

This commit is contained in:
2026-05-11 13:10:45 +08:00
parent cd05e3a4f8
commit cf78d71feb
2 changed files with 33 additions and 79 deletions

View File

@@ -165,7 +165,13 @@ class PayrollRun extends BaseModel
'per_day_salary' => $salaryBreakdown['daily_rate'],
'unpaid_leave_deduction' => $summary['absence_amount'] ?? 0,
'earnings_breakdown' => $salaryBreakdown['earnings'],
'earnings_breakdown' => array_merge($salaryBreakdown['earnings'], [
'meta' => [
'daily_rate' => $salaryBreakdown['daily_rate'],
'pay_type' => $salaryBreakdown['pay_type'],
],
'summary' => $summary
]),
'deductions_breakdown' => $salaryBreakdown['deductions'],
'created_by' => $this->created_by,
]);

View File

@@ -179,41 +179,18 @@
$summary = $rawEarnings['summary'] ?? [];
unset($rawEarnings['meta'], $rawEarnings['summary']);
$earnings = array_values($rawEarnings);
$deductions = array_values($payrollEntry->deductions_breakdown ?? []);
// Filter out zero values (except Basic Salary) so they don't clutter the payslip
$earnings = array_filter(array_values($rawEarnings), function($item) {
return strtolower($item['name']) === 'basic salary' || (float)$item['amount'] > 0;
});
$deductions = array_filter(array_values($payrollEntry->deductions_breakdown ?? []), function($item) {
return (float)$item['amount'] > 0;
});
if (!function_exists('extractAmount')) {
function extractAmount($array, $keywords) {
$keywords = (array) $keywords;
foreach ($array as $item) {
if (!isset($item['name'])) continue;
foreach ($keywords as $keyword) {
if (stripos($item['name'], $keyword) !== false) {
return $item['amount'];
}
}
}
return 0;
}
}
// Map dynamic earnings natively
$basicSalary = extractAmount($earnings, 'Basic Salary') ?: $payrollEntry->basic_salary;
$nightDiff = extractAmount($earnings, 'Night Diff');
$overtime = extractAmount($earnings, ['Overtime', 'Reg. OT']);
$holidayPay = extractAmount($earnings, 'Holiday');
$adjustment = extractAmount($earnings, 'Adjustment');
// Map explicit deductions natively
$undertimeDed = extractAmount($deductions, 'Undertime');
$sss = extractAmount($deductions, 'SSS');
$phic = extractAmount($deductions, 'PhilHealth');
$hdmf = extractAmount($deductions, 'Pag-IBIG');
$tax = extractAmount($deductions, ['Tax', 'Withholding']);
// Explicit penalties
$tardiness = extractAmount($deductions, ['Late', 'Tardiness']);
$absences = extractAmount($deductions, 'Absence');
// Re-index after filtering
$earnings = array_values($earnings);
$deductions = array_values($deductions);
$payType = $meta['pay_type'] ?? 'Monthly';
@@ -264,36 +241,21 @@
<th width="35%">DEDUCTIONS</th>
<th width="15%" class="amt">AMOUNT</th>
</tr>
<tr>
<td>Basic Salary</td>
<td class="amt">{{ formatCurrency($basicSalary) }}</td>
<td>Undertime Deduction</td>
<td class="amt">{{ formatCurrency($undertimeDed) }}</td>
</tr>
<tr>
<td style="text-transform: uppercase;">Night Differential</td>
<td class="amt">{{ formatCurrency($nightDiff) }}</td>
<td>SSS Contribution (EE)</td>
<td class="amt">{{ formatCurrency($sss) }}</td>
</tr>
<tr>
<td style="text-transform: uppercase;">Overtime</td>
<td class="amt">{{ formatCurrency($overtime) }}</td>
<td>PhilHealth Contribution (EE)</td>
<td class="amt">{{ formatCurrency($phic) }}</td>
</tr>
<tr>
<td style="text-transform: uppercase;">Holiday Pay</td>
<td class="amt">{{ formatCurrency($holidayPay) }}</td>
<td>Pag-IBIG Contribution (EE)</td>
<td class="amt">{{ formatCurrency($hdmf) }}</td>
</tr>
<tr>
<td style="text-transform: uppercase;">Adjustment +</td>
<td class="amt">{{ formatCurrency($adjustment) }}</td>
<td>Withholding Tax</td>
<td class="amt">{{ formatCurrency($tax) }}</td>
</tr>
@php
$maxRows = max(count($earnings), count($deductions));
@endphp
@for ($i = 0; $i < $maxRows; $i++)
@php
$earn = $earnings[$i] ?? null;
$ded = $deductions[$i] ?? null;
@endphp
<tr>
<td style="text-transform: uppercase;">{{ $earn ? $earn['name'] : '' }}</td>
<td class="amt">{{ $earn ? formatCurrency($earn['amount']) : '' }}</td>
<td style="text-transform: uppercase;">{{ $ded ? $ded['name'] : '' }}</td>
<td class="amt">{{ $ded ? formatCurrency($ded['amount']) : '' }}</td>
</tr>
@endfor
<tr class="total-row">
<td>Total Earnings</td>
<td class="amt">{{ formatCurrency($payrollEntry->gross_pay) }}</td>
@@ -302,20 +264,6 @@
</tr>
</table>
<!-- Explicit Penalty Block as per Mockup -->
<table width="100%" style="margin-bottom: 20px;">
<tr>
<td width="50%"></td>
<td width="35%" style="text-transform: uppercase; font-weight: bold; border: 1px solid #ddd; padding: 4px 12px; background: #fff;">Tardiness</td>
<td width="15%" style="border: 1px solid #ddd; padding: 4px 12px; text-align: right; background: #fff; font-weight: bold;">{{ formatCurrency($tardiness) }}</td>
</tr>
<tr>
<td width="50%"></td>
<td width="35%" style="text-transform: uppercase; font-weight: bold; border: 1px solid #ddd; padding: 4px 12px; border-top: none; background: #fff;">Undertime</td>
<td width="15%" style="border: 1px solid #ddd; padding: 4px 12px; border-top: none; text-align: right; background: #fff; font-weight: bold;">{{ formatCurrency($undertimeDed) }}</td>
</tr>
</table>
<div class="att-summary-title">Attendance Summary</div>
<table class="att-grid">
<tr>