'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['fee_structure_id'])) { $response['message'] = 'Invalid JSON payload or missing student_id/fee_structure_id.'; http_response_code(400); echo json_encode($response); exit; } $pdo = db(); try { $pdo->beginTransaction(); // 1. Fetch Fee Structure details $stmt_fsl = $pdo->prepare("SELECT fsl.amount, a.account_code FROM fee_structure_lines fsl JOIN accounts a ON fsl.revenue_account_id = a.id WHERE fsl.fee_structure_id = ?"); $stmt_fsl->execute([$input['fee_structure_id']]); $fee_structure_lines = $stmt_fsl->fetchAll(PDO::FETCH_ASSOC); if (empty($fee_structure_lines)) throw new Exception('Fee structure has no lines or revenue accounts not found.'); // 2. Create Invoice $total_amount = array_sum(array_column($fee_structure_lines, 'amount')); $invoice_id = uuid_v4(); $invoice_number = 'INV-' . time(); $invoice_date = $input['invoice_date'] ?? date('Y-m-d'); $due_date = $input['due_date'] ?? date('Y-m-d', strtotime('+30 days')); $stmt_inv = $pdo->prepare( "INSERT INTO invoices (id, student_id, invoice_number, invoice_date, due_date, total_amount, status) VALUES (?, ?, ?, ?, ?, ?, 'Posted')" ); $stmt_inv->execute([$invoice_id, $input['student_id'], $invoice_number, $invoice_date, $due_date, $total_amount]); // Don't need to insert invoice lines manually if they are based on fee structure, can be joined. // For simplicity, let's assume the previous logic of creating them was desired. // Re-creating invoice lines for atomicity. foreach ($fee_structure_lines as $line) { $stmt_inv_line = $pdo->prepare( "INSERT INTO invoice_lines (id, invoice_id, description, amount, revenue_account_id) VALUES (?, ?, ?, ?, (SELECT id from accounts where account_code = ?))" ); $stmt_inv_line->execute([uuid_v4(), $invoice_id, 'Fee', $line['amount'], $line['account_code']]); } // 3. Prepare and Create Journal Entry $journal_lines = []; // Debit AR $journal_lines[] = [ 'account_code' => '1200-AR-STUDENTS', 'type' => 'DEBIT', 'amount' => $total_amount ]; // Credit Revenue accounts foreach ($fee_structure_lines as $line) { $journal_lines[] = [ 'account_code' => $line['account_code'], 'type' => 'CREDIT', 'amount' => $line['amount'] ]; } $journal_payload = [ 'entry_date' => $invoice_date, 'description' => "Invoice {$invoice_number} for student {$input['student_id']}", 'lines' => $journal_lines ]; $journal_result = post_journal_entry($pdo, $journal_payload); if (empty($journal_result['journal_entry_id'])) { throw new Exception("Failed to post journal entry: " . ($journal_result['error'] ?? 'Unknown error')); } $journal_entry_id = $journal_result['journal_entry_id']; // Link Invoice to Journal Entry $stmt_inv_update = $pdo->prepare("UPDATE invoices SET journal_entry_id = ? WHERE id = ?"); $stmt_inv_update->execute([$journal_entry_id, $invoice_id]); // Log audit trail log_audit_trail($pdo, 'create_invoice', ['invoice_id' => $invoice_id, 'student_id' => $input['student_id'], 'total_amount' => $total_amount]); $pdo->commit(); $response = [ 'status' => 'success', 'message' => 'Invoice created and journal entry posted successfully.', 'data' => [ 'invoice_id' => $invoice_id, 'invoice_number' => $invoice_number, 'journal_entry_id' => $journal_entry_id ] ]; http_response_code(201); } catch (Exception $e) { if ($pdo->inTransaction()) $pdo->rollBack(); $response['message'] = 'Error creating invoice: ' . $e->getMessage(); http_response_code(500); } } echo json_encode($response);