'Missing required fields for journal entry']; } // --- Accounting Period Control --- $stmt_period_check = $pdo->prepare( "SELECT COUNT(*) FROM accounting_periods WHERE status = 'closed' AND :entry_date BETWEEN start_date AND end_date" ); $stmt_period_check->execute([':entry_date' => $payload['entry_date']]); if ($stmt_period_check->fetchColumn() > 0) { return ['error' => 'Cannot post transaction to a closed accounting period.']; } // --- End Control --- $total_debits = 0; $total_credits = 0; foreach ($payload['lines'] as $line) { $debit = isset($line['debit']) ? $line['debit'] : 0; $credit = isset($line['credit']) ? $line['credit'] : 0; $total_debits += $debit; $total_credits += $credit; } if (round($total_debits, 2) !== round($total_credits, 2) || $total_debits === 0) { return ['error' => 'Debits and credits must balance and not be zero.']; } $journal_entry_id = uuid_v4(); $stmt_je = $pdo->prepare("INSERT INTO journal_entries (id, entry_date, description, status) VALUES (?, ?, ?, 'posted')"); $stmt_je->execute([$journal_entry_id, $payload['entry_date'], $payload['description']]); $stmt_lines = $pdo->prepare("INSERT INTO journal_lines (id, journal_entry_id, account_code, debit_amount, credit_amount) VALUES (?, ?, ?, ?, ?)"); $stmt_subledger = $pdo->prepare("INSERT INTO journal_line_subledgers (journal_line_id, subledger_id) VALUES (?, ?)"); foreach ($payload['lines'] as $line) { $journal_line_id = uuid_v4(); $debit = $line['debit'] ?? 0; $credit = $line['credit'] ?? 0; $stmt_lines->execute([$journal_line_id, $journal_entry_id, $line['account_code'], $debit, $credit]); if (!empty($line['subledger_id'])) { $stmt_subledger->execute([$journal_line_id, $line['subledger_id']]); } } // Log audit trail log_audit_trail($pdo, 'post_journal_entry', ['journal_entry_id' => $journal_entry_id, 'description' => $payload['description']]); return ['success' => true, 'journal_entry_id' => $journal_entry_id]; } catch (PDOException $e) { return ['error' => 'Database error in post_journal_entry: ' . $e->getMessage()]; } }