352 lines
12 KiB
PHP
Executable File
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>
|