tuloslaskelma.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <?php
  2. header('Content-Type: application/json');
  3. header('Access-Control-Allow-Origin: *');
  4. header('Access-Control-Allow-Methods: GET, OPTIONS');
  5. header('Access-Control-Allow-Headers: Content-Type, Authorization');
  6. require_once __DIR__ . '/../config/database.php';
  7. require_once __DIR__ . '/../models/AccountingEntry.php';
  8. // Handle preflight requests
  9. if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
  10. exit(0);
  11. }
  12. try {
  13. $database = new Database();
  14. $conn = $database->getConnection();
  15. if (!$conn) {
  16. throw new Exception('Database connection failed');
  17. }
  18. // Get query parameters
  19. $period = $_GET['period'] ?? 'current';
  20. $month = $_GET['month'] ?? null;
  21. $year = $_GET['year'] ?? null;
  22. // Determine date range based on period
  23. $dateConditions = getDateConditions($period, $month, $year);
  24. // Use AccountingEntry model to get data with category information
  25. $sql = "SELECT
  26. ae.*,
  27. ac.category_name,
  28. ac.category_type,
  29. ac.vat_percentage as category_vat,
  30. acgn.category_group_name
  31. FROM accounting_entries ae
  32. LEFT JOIN accounting_categories ac ON ae.category = ac.category_code
  33. LEFT JOIN accounting_category_group_names acgn ON SUBSTRING(ae.category, 1, 3) = acgn.category_group_code
  34. WHERE ae.entry_date BETWEEN :start_date AND :end_date
  35. ORDER BY ae.entry_type, ac.category_code";
  36. $stmt = $conn->prepare($sql);
  37. $stmt->bindParam(':start_date', $dateConditions['start_date']);
  38. $stmt->bindParam(':end_date', $dateConditions['end_date']);
  39. $stmt->execute();
  40. $entries = $stmt->fetchAll(PDO::FETCH_ASSOC);
  41. // Group entries by category for profit and loss statement
  42. $tuloslaskelmaData = groupEntriesForTuloslaskelma($entries);
  43. // Calculate totals
  44. $totals = calculateTotals($tuloslaskelmaData);
  45. // Get previous period data for comparison
  46. $previousDateConditions = getPreviousDateConditions($period, $month, $year);
  47. $previousData = getPreviousPeriodData($conn, $previousDateConditions);
  48. // Combine current and previous data
  49. $result = [
  50. 'period' => $period,
  51. 'date_range' => $dateConditions,
  52. 'current_period' => array_merge($tuloslaskelmaData, $totals),
  53. 'previous_period' => $previousData,
  54. 'comparison' => calculateComparison($tuloslaskelmaData, $previousData)
  55. ];
  56. echo json_encode([
  57. 'success' => true,
  58. 'data' => $result
  59. ]);
  60. } catch (Exception $e) {
  61. http_response_code(500);
  62. echo json_encode([
  63. 'success' => false,
  64. 'message' => $e->getMessage()
  65. ]);
  66. }
  67. function getDateConditions($period, $month, $year) {
  68. $currentYear = $year ?: date('Y');
  69. $currentMonth = $month ? date('m', strtotime($month)) : date('m');
  70. switch ($period) {
  71. case 'month':
  72. return [
  73. 'start_date' => "$currentYear-$currentMonth-01",
  74. 'end_date' => date('Y-m-t', strtotime("$currentYear-$currentMonth-01"))
  75. ];
  76. case 'quarter':
  77. $quarter = ceil($currentMonth / 3);
  78. $startMonth = ($quarter - 1) * 3 + 1;
  79. $endMonth = $quarter * 3;
  80. return [
  81. 'start_date' => "$currentYear-" . str_pad($startMonth, 2, '0', STR_PAD_LEFT) . "-01",
  82. 'end_date' => "$currentYear-" . str_pad($endMonth, 2, '0', STR_PAD_LEFT) . "-" . date('t', strtotime("$currentYear-$endMonth-01"))
  83. ];
  84. case 'year':
  85. return [
  86. 'start_date' => "$currentYear-01-01",
  87. 'end_date' => "$currentYear-12-31"
  88. ];
  89. case 'current':
  90. default:
  91. return [
  92. 'start_date' => date('Y-01-01'),
  93. 'end_date' => date('Y-m-d')
  94. ];
  95. }
  96. }
  97. function getPreviousDateConditions($period, $month, $year) {
  98. $currentYear = $year ?: date('Y');
  99. $currentMonth = $month ? date('m', strtotime($month)) : date('m');
  100. switch ($period) {
  101. case 'month':
  102. $previousDate = date('Y-m', strtotime("$currentYear-$currentMonth-01 -1 month"));
  103. $prevYear = date('Y', strtotime($previousDate));
  104. $prevMonth = date('m', strtotime($previousDate));
  105. return [
  106. 'start_date' => "$prevYear-$prevMonth-01",
  107. 'end_date' => date('Y-m-t', strtotime("$prevYear-$prevMonth-01"))
  108. ];
  109. case 'quarter':
  110. $quarter = ceil($currentMonth / 3);
  111. if ($quarter == 1) {
  112. $prevYear = $currentYear - 1;
  113. $prevQuarter = 4;
  114. } else {
  115. $prevYear = $currentYear;
  116. $prevQuarter = $quarter - 1;
  117. }
  118. $startMonth = ($prevQuarter - 1) * 3 + 1;
  119. $endMonth = $prevQuarter * 3;
  120. return [
  121. 'start_date' => "$prevYear-" . str_pad($startMonth, 2, '0', STR_PAD_LEFT) . "-01",
  122. 'end_date' => "$prevYear-" . str_pad($endMonth, 2, '0', STR_PAD_LEFT) . "-" . date('t', strtotime("$prevYear-$endMonth-01"))
  123. ];
  124. case 'year':
  125. return [
  126. 'start_date' => ($currentYear - 1) . "-01-01",
  127. 'end_date' => ($currentYear - 1) . "-12-31"
  128. ];
  129. case 'current':
  130. default:
  131. return [
  132. 'start_date' => (date('Y') - 1) . "-01-01",
  133. 'end_date' => (date('Y') - 1) . "-12-31"
  134. ];
  135. }
  136. }
  137. function groupEntriesForTuloslaskelma($entries) {
  138. $grouped = [];
  139. // First, group by account (first three digits)
  140. $accountGroups = [];
  141. foreach ($entries as $entry) {
  142. $accountCode = substr($entry['category'], 0, 3);
  143. if (!isset($accountGroups[$accountCode])) {
  144. $accountGroups[$accountCode] = [
  145. 'account_code' => $accountCode,
  146. 'account_group_name' => $entry['category_group_name'] ?: 'Unknown Group',
  147. 'entries' => []
  148. ];
  149. }
  150. $accountGroups[$accountCode]['entries'][] = $entry;
  151. }
  152. // Now, group account groups by Finnish accounting categories
  153. foreach ($accountGroups as $accountCode => $accountGroup) {
  154. $totalAmount = 0;
  155. foreach ($accountGroup['entries'] as $entry) {
  156. $totalAmount += floatval($entry['tax_free_amount']);
  157. }
  158. $category = [
  159. 'code' => $accountCode,
  160. 'name' => $accountGroup['account_group_name'],
  161. 'currentAmount' => $totalAmount
  162. ];
  163. // Group by Finnish accounting categories
  164. if ($accountGroup['entries'][0]['entry_type'] === 'Tulo') {
  165. if (strpos($accountCode, '30') === 0) {
  166. $grouped['sales_revenue'][] = $category;
  167. } else {
  168. $grouped['other_revenue'][] = $category;
  169. }
  170. } else { // Kulu (Expense)
  171. switch ($accountCode) {
  172. case '333':
  173. $grouped['cost_of_goods'][] = $category;
  174. break;
  175. case '335':
  176. $grouped['personnel_expenses'][] = $category;
  177. break;
  178. case '336':
  179. $grouped['personnel_expenses'][] = $category;
  180. break;
  181. case '337':
  182. $grouped['depreciation'][] = $category;
  183. break;
  184. case '365':
  185. $grouped['tax_expenses'][] = $category;
  186. break;
  187. default:
  188. $grouped['other_expenses'][] = $category;
  189. break;
  190. }
  191. }
  192. }
  193. return $grouped;
  194. }
  195. function calculateTotals($data) {
  196. $totals = [];
  197. foreach ($data as $category => $items) {
  198. $total = 0;
  199. foreach ($items as $item) {
  200. $total += $item['currentAmount'];
  201. }
  202. $totals[$category . '_total'] = [
  203. 'currentAmount' => $total
  204. ];
  205. }
  206. // Calculate derived totals
  207. $salesRevenueTotal = $totals['sales_revenue_total']['currentAmount'] ?? 0;
  208. $otherRevenueTotal = $totals['other_revenue_total']['currentAmount'] ?? 0;
  209. $totalRevenue = $salesRevenueTotal + $otherRevenueTotal;
  210. $costOfGoodsTotal = $totals['cost_of_goods_total']['currentAmount'] ?? 0;
  211. $personnelExpensesTotal = $totals['personnel_expenses_total']['currentAmount'] ?? 0;
  212. $otherExpensesTotal = $totals['other_expenses_total']['currentAmount'] ?? 0;
  213. $depreciationTotal = $totals['depreciation_total']['currentAmount'] ?? 0;
  214. $taxExpensesTotal = $totals['tax_expenses_total']['currentAmount'] ?? 0;
  215. $totalExpenses = $costOfGoodsTotal + $personnelExpensesTotal + $otherExpensesTotal + $depreciationTotal;
  216. $grossProfit = $salesRevenueTotal - $costOfGoodsTotal;
  217. $profitBeforeTax = $totalRevenue - $totalExpenses;
  218. $netProfit = $profitBeforeTax - $taxExpensesTotal;
  219. $totals['total_revenue'] = ['currentAmount' => $totalRevenue];
  220. $totals['total_expenses'] = ['currentAmount' => $totalExpenses];
  221. $totals['gross_profit'] = ['currentAmount' => $grossProfit];
  222. $totals['profit_before_tax'] = ['currentAmount' => $profitBeforeTax];
  223. $totals['net_profit'] = ['currentAmount' => $netProfit];
  224. return $totals;
  225. }
  226. function getPreviousPeriodData($conn, $dateConditions) {
  227. $sql = "SELECT
  228. ae.*,
  229. ac.category_name,
  230. ac.category_type,
  231. ac.vat_percentage as category_vat,
  232. acgn.category_group_name
  233. FROM accounting_entries ae
  234. LEFT JOIN accounting_categories ac ON ae.category = ac.category_code
  235. LEFT JOIN accounting_category_group_names acgn ON SUBSTRING(ae.category, 1, 3) = acgn.category_group_code
  236. WHERE ae.entry_date BETWEEN :start_date AND :end_date
  237. ORDER BY ae.entry_type, ac.category_code";
  238. $stmt = $conn->prepare($sql);
  239. $stmt->bindParam(':start_date', $dateConditions['start_date']);
  240. $stmt->bindParam(':end_date', $dateConditions['end_date']);
  241. $stmt->execute();
  242. $entries = $stmt->fetchAll(PDO::FETCH_ASSOC);
  243. $grouped = groupEntriesForTuloslaskelma($entries);
  244. // Add previous amounts to categories
  245. foreach ($grouped as $category => $items) {
  246. foreach ($items as $index => $item) {
  247. $grouped[$category][$index]['previousAmount'] = $item['currentAmount'];
  248. // Also add isGroup flag for frontend
  249. $grouped[$category][$index]['isGroup'] = true;
  250. }
  251. }
  252. return $grouped;
  253. }
  254. function calculateComparison($current, $previous) {
  255. $comparison = [];
  256. foreach ($current as $category => $items) {
  257. $comparison[$category] = [];
  258. foreach ($items as $index => $item) {
  259. $prevAmount = 0;
  260. if (isset($previous[$category][$index])) {
  261. $prevAmount = $previous[$category][$index]['currentAmount'] ?? 0;
  262. }
  263. $change = $item['currentAmount'] - $prevAmount;
  264. $changePercent = $prevAmount != 0 ? ($change / $prevAmount) * 100 : 0;
  265. $comparison[$category][$index] = [
  266. 'change' => $change,
  267. 'changePercent' => $changePercent
  268. ];
  269. }
  270. }
  271. return $comparison;
  272. }
  273. ?>