37241-vm/api/payments_post.php
2026-01-03 08:40:36 +00:00

124 lines
5.5 KiB
PHP

<?php
// api/payments_post.php
header('Content-Type: application/json');
require_once __DIR__ . '/../db/config.php';
require_once __DIR__ . '/../includes/uuid.php';
require_once __DIR__ . '/../includes/audit_helpers.php';
$response = [
'status' => '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);