Spaces:
Running
Running
| /** | |
| * API Endpoint: Get Last Payment Receipt Image | |
| * | |
| * Returns the receipt image (PNG) of the last payment made by a student. | |
| * | |
| * Method: GET | |
| * URL: /api/students/last_receipt?student_id=... | |
| * | |
| * Response: PNG image (binary) | |
| * | |
| * Error Response: JSON with error message | |
| */ | |
| require_once '../../db_config.php'; | |
| require_once '../../includes/ReceiptGenerator.php'; | |
| require_once '../../includes/ApiValidator.php'; | |
| require_once '../../config/api_config.php'; | |
| // Initialize validator | |
| $validator = new ApiValidator($pdo, defined('API_KEYS') ? API_KEYS : []); | |
| // 1. Validate Request | |
| $validation = $validator->validateRequest(['GET']); | |
| if (!$validation['valid']) { | |
| http_response_code($validation['http_code']); | |
| header('Content-Type: application/json'); | |
| echo json_encode(['status' => 'error', 'message' => $validation['error']]); | |
| exit; | |
| } | |
| // 2. Validate Input Parameters | |
| $studentId = trim($_GET['student_id'] ?? ''); | |
| if (empty($studentId)) { | |
| http_response_code(400); | |
| header('Content-Type: application/json'); | |
| echo json_encode(['status' => 'error', 'message' => 'Student ID is required']); | |
| exit; | |
| } | |
| // 3. Validate Student Exists | |
| $studentCheck = $validator->validateStudentExists($studentId); | |
| if (!$studentCheck['valid']) { | |
| http_response_code($studentCheck['http_code']); | |
| header('Content-Type: application/json'); | |
| echo json_encode(['status' => 'error', 'message' => $studentCheck['error']]); | |
| exit; | |
| } | |
| // Prevent output from messing up image headers | |
| ob_start(); | |
| ini_set('display_errors', 0); | |
| error_reporting(E_ALL & ~E_DEPRECATED & ~E_NOTICE); | |
| try { | |
| // 4. Fetch Last Payment Receipt Number | |
| $sqlLastReceipt = "SELECT receipt_no, payment_date | |
| FROM tb_account_payment_registers | |
| WHERE student_id = :student_id | |
| ORDER BY payment_date DESC, id DESC | |
| LIMIT 1"; | |
| $stmt = $pdo->prepare($sqlLastReceipt); | |
| $stmt->execute(['student_id' => $studentId]); | |
| $lastPayment = $stmt->fetch(PDO::FETCH_ASSOC); | |
| if (!$lastPayment) { | |
| http_response_code(404); | |
| header('Content-Type: application/json'); | |
| echo json_encode([ | |
| 'status' => 'error', | |
| 'message' => 'No payment records found for this student' | |
| ]); | |
| exit; | |
| } | |
| $receiptNo = $lastPayment['receipt_no']; | |
| $paymentDate = $lastPayment['payment_date']; | |
| // 5. Fetch Receipt Meta Info (Student, Date) | |
| $sqlInfo = "SELECT pr.student_id, pr.payment_date, | |
| sr.last_name, sr.first_name, sr.other_name, sr.student_code, | |
| al.level_name | |
| FROM tb_account_payment_registers pr | |
| JOIN tb_student_registrations sr ON pr.student_id = sr.id | |
| LEFT JOIN tb_academic_levels al ON sr.level_id = al.id | |
| WHERE pr.receipt_no = :receipt_no | |
| LIMIT 1"; | |
| $stmt = $pdo->prepare($sqlInfo); | |
| $stmt->execute(['receipt_no' => $receiptNo]); | |
| $receiptInfo = $stmt->fetch(PDO::FETCH_ASSOC); | |
| if (!$receiptInfo) { | |
| http_response_code(500); | |
| header('Content-Type: application/json'); | |
| echo json_encode([ | |
| 'status' => 'error', | |
| 'message' => 'Receipt information not found' | |
| ]); | |
| exit; | |
| } | |
| $studentId = $receiptInfo['student_id']; | |
| // 6. Fetch All Fees for Student (from receivables) | |
| $sqlFees = "SELECT ar.fee_id, ar.actual_value as amount_billed, | |
| ar.academic_session, ar.term_of_session, | |
| sf.description as fee_description | |
| FROM tb_account_receivables ar | |
| JOIN tb_account_school_fees sf ON ar.fee_id = sf.id | |
| WHERE ar.student_id = :sid | |
| ORDER BY ar.academic_session ASC, ar.term_of_session ASC"; | |
| $stmtFees = $pdo->prepare($sqlFees); | |
| $stmtFees->execute(['sid' => $studentId]); | |
| $allFees = $stmtFees->fetchAll(PDO::FETCH_ASSOC); | |
| $allocations = []; | |
| $receiptTotalPaid = 0; | |
| foreach ($allFees as $fee) { | |
| // Calculate Paid To Date (up to this receipt's date) | |
| $sqlPaid = "SELECT SUM(amount_paid) as total_paid | |
| FROM tb_account_payment_registers | |
| WHERE student_id = :sid | |
| AND fee_id = :fid | |
| AND academic_session = :as | |
| AND term_of_session = :ts | |
| AND payment_date <= :pd"; | |
| $stmtPaid = $pdo->prepare($sqlPaid); | |
| $stmtPaid->execute([ | |
| 'sid' => $studentId, | |
| 'fid' => $fee['fee_id'], | |
| 'as' => $fee['academic_session'], | |
| 'ts' => $fee['term_of_session'], | |
| 'pd' => $paymentDate | |
| ]); | |
| $paidResult = $stmtPaid->fetch(PDO::FETCH_ASSOC); | |
| $paidToDate = floatval($paidResult['total_paid'] ?? 0); | |
| // Calculate Amount paid IN THIS RECEIPT (for total calculation) | |
| $sqlReceiptPay = "SELECT SUM(amount_paid) as receipt_paid | |
| FROM tb_account_payment_registers | |
| WHERE receipt_no = :rno | |
| AND fee_id = :fid | |
| AND academic_session = :as | |
| AND term_of_session = :ts"; | |
| $stmtReceiptPay = $pdo->prepare($sqlReceiptPay); | |
| $stmtReceiptPay->execute([ | |
| 'rno' => $receiptNo, | |
| 'fid' => $fee['fee_id'], | |
| 'as' => $fee['academic_session'], | |
| 'ts' => $fee['term_of_session'] | |
| ]); | |
| $receiptPayResult = $stmtReceiptPay->fetch(PDO::FETCH_ASSOC); | |
| $paidInReceipt = floatval($receiptPayResult['receipt_paid'] ?? 0); | |
| $receiptTotalPaid += $paidInReceipt; | |
| $balance = floatval($fee['amount_billed']) - $paidToDate; | |
| // Condition: Show if (Balance > 0) OR (PaidInReceipt > 0) | |
| // Helps filter out old fully paid fees, but keeps current payments even if they zeroed the balance | |
| if ($balance > 0.001 || $paidInReceipt > 0.001) { | |
| $allocations[] = [ | |
| 'description' => $fee['fee_description'], | |
| 'academic_session' => $fee['academic_session'], | |
| 'term_of_session' => $fee['term_of_session'], | |
| 'amount_billed' => floatval($fee['amount_billed']), | |
| 'amount' => $paidInReceipt, | |
| 'total_paid_to_date' => $paidToDate, | |
| 'balance' => $balance | |
| ]; | |
| } | |
| } | |
| // 7. Prepare data structure for generator | |
| $data = [ | |
| 'receipt_no' => $receiptNo, | |
| 'student_name' => trim($receiptInfo['last_name'] . ' ' . $receiptInfo['first_name'] . ' ' . ($receiptInfo['other_name'] ?? '')), | |
| 'student_code' => $receiptInfo['student_code'], | |
| 'level_name' => $receiptInfo['level_name'] ?? '', | |
| 'payment_date' => $paymentDate, | |
| 'total_paid' => $receiptTotalPaid, | |
| 'allocations' => $allocations | |
| ]; | |
| // 8. Generate Image | |
| $generator = new ReceiptGenerator(); | |
| $imageData = $generator->generate($data); | |
| // 9. Output Image | |
| ob_end_clean(); // Discard any warnings/output buffered so far | |
| header('Content-Type: image/png'); | |
| header('Content-Disposition: inline; filename="receipt_' . $receiptNo . '.png"'); | |
| header('Content-Length: ' . strlen($imageData)); | |
| echo $imageData; | |
| } catch (Exception $e) { | |
| ob_end_clean(); | |
| http_response_code(500); | |
| header('Content-Type: application/json'); | |
| echo json_encode([ | |
| 'status' => 'error', | |
| 'message' => 'Error generating receipt: ' . $e->getMessage() | |
| ]); | |
| } | |