false, 'error' => 'Method not allowed'], 405); } $people = migrateAllPeople(normalizePeopleList(readJsonFile('people.json'))); $actor = requireActivePerson($people); $settings = loadFamilySettings(); if (!bankingEnabled($settings)) { sendJson(['success' => false, 'error' => 'Banking mode is not enabled'], 400); } $month = trim((string) ($_GET['month'] ?? gmdate('Y-m'))); if (!preg_match('/^\d{4}-\d{2}$/', $month)) { sendJson(['success' => false, 'error' => 'month must be YYYY-MM'], 400); } $format = trim((string) ($_GET['format'] ?? 'json')); if (!in_array($format, ['json', 'csv'], true)) { sendJson(['success' => false, 'error' => 'format must be json or csv'], 400); } $personId = trim((string) ($_GET['person_id'] ?? '')); if ($personId === '') { $personId = (string) ($actor['id'] ?? ''); } $isHoh = (($actor['role'] ?? '') === ROLE_HEAD) && isHohVerified(); if (!$isHoh && $personId !== (string) ($actor['id'] ?? '')) { sendJson(['success' => false, 'error' => 'Only verified Head of household can view statements for others'], 403); } $person = null; foreach ($people as $p) { if (($p['id'] ?? '') === $personId) { $person = $p; break; } } if ($person === null) { sendJson(['success' => false, 'error' => 'Person not found'], 404); } $rows = readJsonFile('bank_transactions.json'); if (!is_array($rows)) { $rows = []; } $prefix = $month . '-'; $filtered = []; foreach ($rows as $row) { if (!is_array($row)) { continue; } if ((string) ($row['person_id'] ?? '') !== $personId) { continue; } $createdAt = (string) ($row['created_at'] ?? ''); $ymd = bankingYmd($createdAt); if (strpos($ymd, $prefix) !== 0) { continue; } $filtered[] = $row; } if ($format === 'csv') { header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename="bank_statement_' . $personId . '_' . $month . '.csv"'); $out = fopen('php://output', 'w'); if ($out === false) { exit; } fputcsv($out, ['id', 'created_at', 'type', 'amount', 'category', 'note', 'from_account', 'to_account', 'reversal_of_transaction_id']); foreach ($filtered as $row) { fputcsv($out, [ (string) ($row['id'] ?? ''), (string) ($row['created_at'] ?? ''), (string) ($row['type'] ?? ''), (string) ($row['amount'] ?? ''), (string) ($row['category'] ?? ''), (string) ($row['note'] ?? ''), (string) ($row['from_account'] ?? ''), (string) ($row['to_account'] ?? ''), (string) ($row['reversal_of_transaction_id'] ?? ''), ]); } fclose($out); exit; } sendJson([ 'success' => true, 'month' => $month, 'person' => [ 'id' => (string) ($person['id'] ?? ''), 'name' => (string) ($person['name'] ?? ''), ], 'balances' => [ 'checking' => (float) ($person['checking_balance'] ?? 0), 'savings' => (float) ($person['savings_balance'] ?? 0), 'charity_pending' => (float) ($person['charity_pending_balance'] ?? 0), 'charity_donated_total' => (float) ($person['charity_donated_total'] ?? 0), ], 'transactions' => $filtered, ]);