api.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. <?php
  2. header('Content-Type: application/json; charset=utf-8');
  3. header('Cache-Control: no-store');
  4. $dbPath = __DIR__ . '/_private/pl.db';
  5. if (!file_exists($dbPath) || filesize($dbPath) === 0) {
  6. echo json_encode(['error' => 'Database not initialized. Please upload a CSV file first.']);
  7. exit;
  8. }
  9. try {
  10. $db = new PDO('sqlite:' . $dbPath);
  11. $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  12. $db->exec('PRAGMA query_only = ON');
  13. } catch (Exception $e) {
  14. echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
  15. exit;
  16. }
  17. $action = $_GET['action'] ?? '';
  18. switch ($action) {
  19. case 'months':
  20. $rows = $db->query(
  21. 'SELECT id, label, year, month_num, sort_order, is_total, is_partial
  22. FROM months ORDER BY sort_order'
  23. )->fetchAll(PDO::FETCH_ASSOC);
  24. foreach ($rows as &$r) {
  25. $r['id'] = (int)$r['id'];
  26. $r['year'] = $r['year'] !== null ? (int)$r['year'] : null;
  27. $r['month_num'] = $r['month_num'] !== null ? (int)$r['month_num'] : null;
  28. $r['sort_order']= (int)$r['sort_order'];
  29. $r['is_total'] = (int)$r['is_total'];
  30. $r['is_partial']= (int)$r['is_partial'];
  31. }
  32. echo json_encode($rows, JSON_UNESCAPED_UNICODE);
  33. break;
  34. case 'data':
  35. $idsParam = $_GET['ids'] ?? '';
  36. $idList = array_unique(array_filter(array_map('intval', explode(',', $idsParam))));
  37. if (empty($idList)) {
  38. echo json_encode(['columns' => [], 'rows' => []]);
  39. break;
  40. }
  41. $ph = implode(',', array_fill(0, count($idList), '?'));
  42. // Columns in chronological order
  43. $colStmt = $db->prepare(
  44. "SELECT id, label, year, month_num, sort_order, is_total, is_partial
  45. FROM months WHERE id IN ($ph) ORDER BY sort_order"
  46. );
  47. $colStmt->execute($idList);
  48. $columns = $colStmt->fetchAll(PDO::FETCH_ASSOC);
  49. foreach ($columns as &$c) {
  50. $c['id'] = (int)$c['id'];
  51. $c['year'] = $c['year'] !== null ? (int)$c['year'] : null;
  52. $c['month_num'] = $c['month_num'] !== null ? (int)$c['month_num'] : null;
  53. $c['is_total'] = (int)$c['is_total'];
  54. $c['is_partial']= (int)$c['is_partial'];
  55. }
  56. // All account rows
  57. $accounts = $db->query(
  58. 'SELECT id, name, row_type, indent_level FROM accounts ORDER BY sort_order'
  59. )->fetchAll(PDO::FETCH_ASSOC);
  60. // Values for the requested months
  61. $valStmt = $db->prepare(
  62. "SELECT account_id, month_id, value FROM pl_values WHERE month_id IN ($ph)"
  63. );
  64. $valStmt->execute($idList);
  65. $values = [];
  66. while ($v = $valStmt->fetch(PDO::FETCH_ASSOC)) {
  67. $values[(int)$v['account_id']][(int)$v['month_id']] = (float)$v['value'];
  68. }
  69. $rows = [];
  70. foreach ($accounts as $a) {
  71. $aid = (int)$a['id'];
  72. $vals = $values[$aid] ?? [];
  73. // Convert integer keys to string keys for clean JSON object
  74. $out = new stdClass();
  75. foreach ($vals as $mid => $val) {
  76. $out->$mid = $val;
  77. }
  78. $rows[] = [
  79. 'id' => $aid,
  80. 'name' => $a['name'],
  81. 'type' => $a['row_type'],
  82. 'indent' => (int)$a['indent_level'],
  83. 'values' => $out,
  84. ];
  85. }
  86. echo json_encode(
  87. ['columns' => $columns, 'rows' => $rows],
  88. JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION
  89. );
  90. break;
  91. default:
  92. http_response_code(400);
  93. echo json_encode(['error' => 'Unknown action']);
  94. }