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

127 lines
4.2 KiB
PHP

<?php
header('Content-Type: application/json');
require_once __DIR__ . '../../../db/config.php';
//
// ## Balance Sheet Report
//
// Purpose: Show financial position (Assets = Liabilities + Equity) at a specific date.
//
// ---
//
// **Rule (Absolute):** Reads ONLY from `accounts` and `journal_lines` for posted entries.
// It includes retained earnings (net income from prior periods).
//
$pdo = db();
$as_of_date = $_GET['as_of_date'] ?? date('Y-m-d');
try {
// --- 1. Calculate Net Income (Retained Earnings) ---
// This is the sum of all revenue and expense accounts from the beginning of time up to the report date.
$retained_earnings_sql = <<<SQL
SELECT
SUM(CASE
WHEN a.account_type = 'revenue' THEN (jl.credit_amount - jl.debit_amount)
WHEN a.account_type = 'expense' THEN (jl.debit_amount - jl.credit_amount)
ELSE 0
END) as net_income
FROM journal_lines jl
JOIN accounts a ON jl.account_code = a.account_code
JOIN journal_entries je ON jl.journal_entry_id = je.id
WHERE je.status = 'posted' AND a.account_type IN ('revenue', 'expense') AND je.entry_date <= :as_of_date;
SQL;
$stmt_re = $pdo->prepare($retained_earnings_sql);
$stmt_re->execute([':as_of_date' => $as_of_date]);
$retained_earnings = $stmt_re->fetchColumn() ?: 0;
// --- 2. Get Balances for Asset, Liability, and Equity Accounts ---
$main_sql = <<<SQL
SELECT
a.account_code,
a.account_name,
a.account_type,
a.normal_balance,
SUM(CASE
WHEN a.normal_balance = 'debit' THEN jl.debit_amount - jl.credit_amount
ELSE jl.credit_amount - jl.debit_amount
END) as balance
FROM accounts a
JOIN journal_lines jl ON a.account_code = jl.account_code
JOIN journal_entries je ON jl.journal_entry_id = je.id
WHERE
je.status = 'posted'
AND a.account_type IN ('asset', 'liability', 'equity')
AND je.entry_date <= :as_of_date
GROUP BY a.account_code, a.account_name, a.account_type, a.normal_balance
HAVING balance != 0
ORDER BY a.account_type, a.account_code;
SQL;
$stmt_main = $pdo->prepare($main_sql);
$stmt_main->execute([':as_of_date' => $as_of_date]);
$report_lines = $stmt_main->fetchAll(PDO::FETCH_ASSOC);
// --- 3. Structure the report and calculate totals ---
$assets_total = 0;
$liabilities_total = 0;
$equity_total = 0;
$assets = [];
$liabilities = [];
$equity = [];
foreach ($report_lines as $line) {
switch ($line['account_type']) {
case 'asset':
$assets[] = $line;
$assets_total += $line['balance'];
break;
case 'liability':
$liabilities[] = $line;
$liabilities_total += $line['balance'];
break;
case 'equity':
$equity[] = $line;
$equity_total += $line['balance'];
break;
}
}
// Add retained earnings to equity section
$equity_total += $retained_earnings;
$total_liabilities_and_equity = $liabilities_total + $equity_total;
echo json_encode([
'success' => true,
'report_name' => 'Balance Sheet',
'generated_at' => date('c'),
'filters' => ['as_of_date' => $as_of_date],
'data' => [
'assets' => $assets,
'liabilities' => $liabilities,
'equity' => array_merge($equity, [[
'account_code' => '3999',
'account_name' => 'Retained Earnings',
'account_type' => 'equity',
'balance' => $retained_earnings
]])
],
'summary' => [
'total_assets' => $assets_total,
'total_liabilities' => $liabilities_total,
'total_equity' => $equity_total,
'total_liabilities_and_equity' => $total_liabilities_and_equity,
'is_balanced' => abs($assets_total - $total_liabilities_and_equity) < 0.0001
]
]);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'error' => 'Database error: ' . $e->getMessage()
]);
}