prepare( "SELECT ps.*, pr.pay_period_month, pr.pay_period_year, e.first_name, e.last_name, e.position, c.name as company_name FROM payslips ps JOIN payroll_runs pr ON ps.payroll_run_id = pr.id JOIN employees e ON ps.employee_id = e.id JOIN companies c ON pr.company_id = c.id WHERE ps.id = ? AND c.id = ?" ); $stmt->execute([$payslip_id, $company_id]); $payslip = $stmt->fetch(); if (!$payslip) { http_response_code(404); die('Payslip not found or access denied.'); } // Fetch Payslip Items $stmt = $pdo->prepare("SELECT * FROM payslip_items WHERE payslip_id = ? ORDER BY type, description"); $stmt->execute([$payslip_id]); $items = $stmt->fetchAll(); } catch (Exception $e) { http_response_code(500); die('Database error: ' . $e->getMessage()); } // PDF Generation class MYPDF extends TCPDF { public function Header() { $this->SetFont('helvetica', 'B', 20); $this->Cell(0, 15, 'Payslip', 0, false, 'C', 0, '', 0, false, 'M', 'M'); } public function Footer() { $this->SetY(-15); $this->SetFont('helvetica', 'I', 8); $this->Cell(0, 10, 'Page '.$this->getAliasNumPage().'/'.$this->getAliasNbPages(), 0, false, 'C', 0, '', 0, false, 'T', 'M'); } } $pdf = new MYPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); $pdf->SetCreator(PDF_CREATOR); $pdf->SetAuthor('GPTPayroll'); $pdf->SetTitle('Payslip'); $pdf->SetSubject('Payslip'); $pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN)); $pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA)); $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED); $pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT); $pdf->SetHeaderMargin(PDF_MARGIN_HEADER); $pdf->SetFooterMargin(PDF_MARGIN_FOOTER); $pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM); $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); $pdf->AddPage(); $pdf->SetFont('helvetica', '', 10); // --- Company & Employee Details --- $html = '
' . htmlspecialchars($payslip['company_name']) . '
Pay Period: ' . htmlspecialchars(date('F Y', mktime(0,0,0, (int)$payslip['pay_period_month'], 1, (int)$payslip['pay_period_year']))) . '
Employee: ' . htmlspecialchars($payslip['first_name'] . ' ' . $payslip['last_name']) . '
Position: ' . htmlspecialchars($payslip['position']) . '

'; $pdf->writeHTML($html, true, false, true, false, ''); // --- Earnings, Deductions, Summary --- $earnings = array_filter($items, function($i) { return $i['type'] == 'earning'; }); $deductions = array_filter($items, function($i) { return $i['type'] == 'deduction' || $i['type'] == 'tax'; }); $html = '
Earnings
' . implode('', array_map(function($item) { return ''; }, $earnings)) . '
' . htmlspecialchars($item['description']) . '' . number_format((float)$item['amount'], 2) . '
Deductions
' . implode('', array_map(function($item) { return ''; }, $deductions)) . '
' . htmlspecialchars($item['description']) . '' . number_format((float)$item['amount'], 2) . '


'; $pdf->writeHTML($html, true, false, true, false, ''); // --- Summary --- $html = '
Gross Pay:
ZMW ' . number_format((float)$payslip['gross_pay'], 2) . '
Total Deductions:
ZMW ' . number_format((float)$payslip['total_deductions'], 2) . '
Net Pay:
ZMW ' . number_format((float)$payslip['net_pay'], 2) . '
'; $pdf->writeHTML($html, true, false, true, false, ''); // --- Output --- $pdf->Output('payslip_' . $payslip_id . '.pdf', 'I');