'error', 'message' => 'Invalid request.' ]; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $input = json_decode(file_get_contents('php://input'), true); if (json_last_error() !== JSON_ERROR_NONE || empty($input['student_id']) || empty($input['amount_received']) || empty($input['payment_method']) || empty($input['cash_account_code']) || empty($input['lines'])) { $response['message'] = 'Invalid JSON payload or missing required fields.'; http_response_code(400); echo json_encode($response); exit; } $pdo = db(); try { $pdo->beginTransaction(); // 1. Fetch Student and Accounts $stmt_student = $pdo->prepare("SELECT subledger_id FROM students WHERE id = ?"); $stmt_student->execute([$input['student_id']]); $student_subledger_id = $stmt_student->fetchColumn(); if (!$student_subledger_id) throw new Exception('Student not found.'); $stmt_ar = $pdo->prepare("SELECT id FROM accounts WHERE account_code = '1200-AR-STUDENTS'"); $stmt_ar->execute(); $ar_account_id = $stmt_ar->fetchColumn(); if (!$ar_account_id) throw new Exception('AR account not found.'); $stmt_cash = $pdo->prepare("SELECT id FROM accounts WHERE account_code = ? AND account_type = 'Asset'"); $stmt_cash->execute([$input['cash_account_code']]); $cash_account_id = $stmt_cash->fetchColumn(); if (!$cash_account_id) throw new Exception('Invalid cash/asset account code.'); // 2. Create Payment $payment_id = uuid_v4(); $payment_date = $input['payment_date'] ?? date('Y-m-d'); $stmt_payment = $pdo->prepare( "INSERT INTO payments (id, student_id, payment_date, amount_received, payment_method, reference_number) VALUES (?, ?, ?, ?, ?, ?)" ); $stmt_payment->execute([$payment_id, $input['student_id'], $payment_date, $input['amount_received'], $input['payment_method'], $input['reference_number'] ?? null]); // 3. Create Payment Lines and Update Invoices $total_applied = 0; foreach ($input['lines'] as $line) { $total_applied += $line['amount_applied']; $stmt_pl = $pdo->prepare("INSERT INTO payment_lines (id, payment_id, invoice_id, amount_applied) VALUES (?, ?, ?, ?)"); $stmt_pl->execute([uuid_v4(), $payment_id, $line['invoice_id'], $line['amount_applied']]); // Check if invoice is fully paid $stmt_inv_total = $pdo->prepare("SELECT total_amount FROM invoices WHERE id = ?"); $stmt_inv_total->execute([$line['invoice_id']]); $invoice_total = $stmt_inv_total->fetchColumn(); $stmt_paid_total = $pdo->prepare("SELECT SUM(amount_applied) FROM payment_lines WHERE invoice_id = ?"); $stmt_paid_total->execute([$line['invoice_id']]); $total_paid_for_invoice = $stmt_paid_total->fetchColumn(); if ($total_paid_for_invoice >= $invoice_total) { $stmt_inv_update = $pdo->prepare("UPDATE invoices SET status = 'Paid' WHERE id = ?"); $stmt_inv_update->execute([$line['invoice_id']]); } } if (bccomp($total_applied, $input['amount_received'], 2) != 0) { throw new Exception('Sum of amounts applied does not match amount received.'); } // 4. Create Journal Entry $journal_entry_id = uuid_v4(); $description = "Payment {$input['reference_number']} for student {$input['student_id']}"; $stmt_je = $pdo->prepare( "INSERT INTO journal_entries (id, entry_date, description, status, posted_at) VALUES (?, ?, ?, 'Posted', NOW())" ); $stmt_je->execute([$journal_entry_id, $payment_date, $description]); // Debit Cash/Bank $stmt_jel_dr = $pdo->prepare("INSERT INTO journal_entry_lines (id, journal_entry_id, account_id, debit) VALUES (?, ?, ?, ?)"); $stmt_jel_dr->execute([uuid_v4(), $journal_entry_id, $cash_account_id, $input['amount_received']]); // Credit AR $stmt_jel_cr = $pdo->prepare( "INSERT INTO journal_entry_lines (id, journal_entry_id, account_id, subledger_id, credit) VALUES (?, ?, ?, ?, ?)" ); $stmt_jel_cr->execute([uuid_v4(), $journal_entry_id, $ar_account_id, $student_subledger_, $input['amount_received']]); // Link Payment to Journal Entry $stmt_payment_update = $pdo->prepare("UPDATE payments SET journal_entry_id = ? WHERE id = ?"); $stmt_payment_update->execute([$journal_entry_id, $payment_id]); // Log audit trail log_audit_trail($pdo, 'record_payment', ['payment_id' => $payment_id, 'student_id' => $input['student_id'], 'amount' => $input['amount_received']]); $pdo->commit(); $response = [ 'status' => 'success', 'message' => 'Payment recorded and journal entry posted successfully.', 'data' => [ 'payment_id' => $payment_id, 'journal_entry_id' => $journal_entry_id ] ]; http_response_code(201); } catch (Exception $e) { if ($pdo->inTransaction()) $pdo->rollBack(); $response['message'] = 'Error recording payment: ' . $e->getMessage(); http_response_code(500); } } echo json_encode($response, JSON_PRETTY_PRINT);