Files
HRM-System/resources/views/payslips/template.blade.php
2026-04-20 00:20:10 +08:00

352 lines
12 KiB
PHP
Executable File

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Payslip - {{ $payrollEntry->employee->name }}</title>
<style>
@page {
margin: 40px;
}
body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 11px;
color: #111;
margin: 0;
padding: 0;
line-height: 1.4;
}
.header {
text-align: center;
margin-bottom: 25px;
}
.company-name {
font-size: 22px;
font-weight: 700;
margin-bottom: 3px;
}
.company-address {
color: #555;
font-size: 11px;
margin-bottom: 15px;
}
.payslip-title {
font-size: 14px;
font-weight: 700;
margin-bottom: 5px;
text-transform: uppercase;
}
.payslip-period {
color: #777;
font-size: 11px;
}
.info-grid {
width: 100%;
margin-bottom: 15px;
border-collapse: collapse;
}
.info-grid td {
padding: 4px 0;
vertical-align: top;
}
.info-label {
font-weight: bold;
color: #555;
width: 15%;
}
.info-value {
width: 35%;
}
.summary-box {
background-color: #f8f9fa;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 12px;
margin-bottom: 20px;
}
.summary-box table {
width: 100%;
border-collapse: collapse;
}
.summary-box td {
padding: 4px 6px;
}
.summary-box .amt {
font-weight: bold;
text-align: right;
}
.main-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
.main-table th, .main-table td {
border: 1px solid #ddd;
padding: 8px 12px;
text-align: left;
}
.main-table th {
font-size: 10px;
font-weight: bold;
background-color: #f1f1f1;
text-transform: uppercase;
}
.main-table .amt {
text-align: right;
}
.main-table .total-row td {
font-weight: bold;
background-color: #fafafa;
}
.att-summary-title {
font-size: 13px;
font-weight: bold;
margin-bottom: 5px;
border-bottom: 2px solid #222;
padding-bottom: 4px;
}
.att-grid {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
border: 1px solid #e0e0e0;
}
.att-grid td {
padding: 8px;
border: 1px solid #e0e0e0;
width: 33.33%;
}
.net-pay-box {
background-color: #f4f6f8;
border: 1px solid #e0e0e0;
padding: 15px;
margin-top: 20px;
}
.net-pay-box table {
width: 100%;
}
.net-label {
font-size: 16px;
font-weight: bold;
}
.net-amount {
text-align: right;
font-size: 20px;
font-weight: bold;
color: #2c5282;
}
.net-words {
font-style: italic;
color: #777;
font-size: 10px;
margin-top: 10px;
}
</style>
</head>
<body>
@php
$rawEarnings = $payrollEntry->earnings_breakdown ?? [];
$meta = $rawEarnings['meta'] ?? [];
$summary = $rawEarnings['summary'] ?? [];
unset($rawEarnings['meta'], $rawEarnings['summary']);
$earnings = array_values($rawEarnings);
$deductions = array_values($payrollEntry->deductions_breakdown ?? []);
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');
$payType = $meta['pay_type'] ?? 'Monthly';
$formatter = class_exists('NumberFormatter') ? new \NumberFormatter("en", \NumberFormatter::SPELLOUT) : null;
$words = $formatter ? ucwords($formatter->format($payrollEntry->net_pay)) . " Pesos Only" : "PHP " . $payrollEntry->net_pay;
if ($formatter && strpos((string)$payrollEntry->net_pay, '.') !== false) {
$parts = explode('.', number_format($payrollEntry->net_pay, 2, '.', ''));
$cents = (int)$parts[1];
if ($cents > 0) {
$words = ucwords($formatter->format((int)$parts[0])) . " Point " . ucwords($formatter->format($cents)) . " Pesos Only";
}
}
@endphp
<div class="header">
<div class="company-name">{{ $companySettings['titleText'] ?? 'Your Company Name' }}</div>
<div class="company-address">{{ $companySettings['companyAddress'] ?? 'Your Company Address' }}</div>
<div class="payslip-title">PAYSLIP</div>
<div class="payslip-period">{{ \Carbon\Carbon::parse($payrollEntry->payrollRun->pay_period_start)->format('M d, Y') }} - {{ \Carbon\Carbon::parse($payrollEntry->payrollRun->pay_period_end)->format('M d, Y') }} ({{ $payType }})</div>
</div>
<table class="info-grid">
<tr>
<td class="info-label">Employee Name:</td>
<td class="info-value">{{ $employee->name }}</td>
<td class="info-label">Employee ID:</td>
<td class="info-value">{{ $employeeData->employee_id ?? '' }}</td>
</tr>
<tr>
<td class="info-label">Department:</td>
<td class="info-value">{{ $employeeData->department->name ?? 'N/A' }}</td>
<td class="info-label">Designation:</td>
<td class="info-value">{{ $employeeData->designation->name ?? 'N/A' }}</td>
</tr>
<tr>
<td class="info-label">Payment Date:</td>
<td class="info-value">{{ \Carbon\Carbon::parse($payrollEntry->payrollRun->pay_date)->format('d M, Y') }}</td>
<td class="info-label">Status:</td>
<td class="info-value">{{ ucfirst($payrollEntry->payslip->status ?? 'Generated') }}</td>
</tr>
</table>
<table class="main-table">
<tr>
<th width="35%">EARNINGS</th>
<th width="15%" class="amt">AMOUNT</th>
<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>
<tr class="total-row">
<td>Total Earnings</td>
<td class="amt">{{ formatCurrency($payrollEntry->gross_pay) }}</td>
<td>Total Deductions</td>
<td class="amt">{{ formatCurrency($payrollEntry->total_deductions) }}</td>
</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;">Absences</td>
<td width="15%" style="border: 1px solid #ddd; padding: 4px 12px; border-top: none; text-align: right; background: #fff; font-weight: bold;">{{ formatCurrency($absences) }}</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>
<td><strong>Days Worked:</strong> {{ $summary['days_worked'] ?? 0 }}</td>
<td><strong>Absences:</strong> {{ $summary['absences'] ?? 0 }}</td>
<td><strong>Leaves:</strong> {{ $summary['leaves'] ?? 0 }}</td>
</tr>
<tr>
<td><strong>Expected Hours:</strong> {{ isset($summary['expected_hours']) ? $summary['expected_hours'].'h' : '0.00h' }}</td>
<td><strong>Rendered Hours:</strong> {{ isset($summary['rendered_hours']) ? $summary['rendered_hours'].'h' : '0.00h' }}</td>
<td><strong>Reg. OT:</strong> {{ isset($summary['reg_ot']) ? $summary['reg_ot'].'h' : '0h' }}</td>
</tr>
<tr>
<td><strong>Night Diff:</strong> {{ isset($summary['night_diff']) ? $summary['night_diff'].'h' : '0h' }}</td>
<td><strong>Holiday:</strong> {{ isset($summary['holiday']) ? $summary['holiday'].'h' : '0h' }}</td>
<td></td>
</tr>
</table>
<div class="net-pay-box">
<table>
<tr>
<td class="net-label">Net Pay: {{ \App\Models\Currency::getDefaultCurrency()->symbol ?? '?' }} {{ number_format($payrollEntry->net_pay, 2) }}</td>
<td class="net-amount"></td>
</tr>
</table>
<div class="net-words">Amount in words: {{ $words }}</div>
</div>
</body>
</html>