127 lines
4.2 KiB
PHP
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()
|
|
]);
|
|
}
|