alv_laskenta.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  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. $report_type = $_GET['report_type'] ?? 'standard'; // 'standard' or 'monthly_summary'
  23. // Determine date range based on period
  24. $dateConditions = getDateConditions($period, $month, $year);
  25. if ($report_type === 'monthly_summary') {
  26. // Generate monthly summary report
  27. error_log("Generating monthly summary for period: " . $period . " from " . $dateConditions['start_date'] . " to " . $dateConditions['end_date']);
  28. $monthlyData = generateMonthlyVATSummary($conn, $dateConditions);
  29. error_log("Monthly summary data: " . json_encode($monthlyData));
  30. echo json_encode([
  31. 'success' => true,
  32. 'data' => [
  33. 'report_type' => 'monthly_summary',
  34. 'period' => $period,
  35. 'date_range' => $dateConditions,
  36. 'monthly_summary' => $monthlyData
  37. ]
  38. ]);
  39. } else {
  40. // Standard VAT calculation
  41. // Fetch accounting entries for VAT calculation
  42. $sql = "SELECT
  43. ae.*,
  44. ac.category_name,
  45. ac.category_type,
  46. ac.vat_percentage as category_vat
  47. FROM accounting_entries ae
  48. LEFT JOIN accounting_categories ac ON ae.category = ac.category_code
  49. WHERE ae.entry_date BETWEEN :start_date AND :end_date
  50. ORDER BY ae.entry_date";
  51. $stmt = $conn->prepare($sql);
  52. $stmt->bindParam(':start_date', $dateConditions['start_date']);
  53. $stmt->bindParam(':end_date', $dateConditions['end_date']);
  54. $stmt->execute();
  55. $entries = $stmt->fetchAll(PDO::FETCH_ASSOC);
  56. // Calculate VAT breakdown
  57. $vatBreakdown = calculateVATBreakdown($entries);
  58. // Get previous period data for comparison
  59. $previousDateConditions = getPreviousDateConditions($period, $month, $year);
  60. $previousData = getPreviousPeriodVATData($conn, $previousDateConditions);
  61. // Combine current and previous data
  62. $result = [
  63. 'period' => $period,
  64. 'date_range' => $dateConditions,
  65. 'vat_breakdown' => $vatBreakdown,
  66. 'previous_period' => $previousData,
  67. 'totals' => calculateVATTotals($vatBreakdown)
  68. ];
  69. echo json_encode([
  70. 'success' => true,
  71. 'data' => $result
  72. ]);
  73. }
  74. } catch (Exception $e) {
  75. http_response_code(500);
  76. echo json_encode([
  77. 'success' => false,
  78. 'message' => $e->getMessage()
  79. ]);
  80. }
  81. function getDateConditions($period, $month, $year) {
  82. $currentYear = $year ?: date('Y');
  83. $currentMonth = $month ? date('m', strtotime($month)) : date('m');
  84. switch ($period) {
  85. case 'month':
  86. return [
  87. 'start_date' => "$currentYear-$currentMonth-01",
  88. 'end_date' => date('Y-m-t', strtotime("$currentYear-$currentMonth-01"))
  89. ];
  90. case 'quarter':
  91. $quarter = isset($_GET['quarter']) ? intval($_GET['quarter']) : ceil($currentMonth / 3);
  92. $startMonth = ($quarter - 1) * 3 + 1;
  93. $endMonth = $quarter * 3;
  94. return [
  95. 'start_date' => "$currentYear-" . str_pad($startMonth, 2, '0', STR_PAD_LEFT) . "-01",
  96. 'end_date' => "$currentYear-" . str_pad($endMonth, 2, '0', STR_PAD_LEFT) . "-" . date('t', strtotime("$currentYear-$endMonth-01"))
  97. ];
  98. case 'year':
  99. return [
  100. 'start_date' => "$currentYear-01-01",
  101. 'end_date' => "$currentYear-12-31"
  102. ];
  103. case 'current':
  104. default:
  105. return [
  106. 'start_date' => date('Y-01-01'),
  107. 'end_date' => date('Y-m-d')
  108. ];
  109. }
  110. }
  111. function getPreviousDateConditions($period, $month, $year) {
  112. $currentYear = $year ?: date('Y');
  113. $currentMonth = $month ? date('m', strtotime($month)) : date('m');
  114. switch ($period) {
  115. case 'month':
  116. $previousDate = date('Y-m', strtotime("$currentYear-$currentMonth-01 -1 month"));
  117. $prevYear = date('Y', strtotime($previousDate));
  118. $prevMonth = date('m', strtotime($previousDate));
  119. return [
  120. 'start_date' => "$prevYear-$prevMonth-01",
  121. 'end_date' => "$prevYear-$prevMonth-" . date('t', strtotime("$prevYear-$prevMonth-01"))
  122. ];
  123. case 'quarter':
  124. $quarter = ceil($currentMonth / 3);
  125. if ($quarter == 1) {
  126. $prevYear = $currentYear - 1;
  127. $prevQuarter = 4;
  128. } else {
  129. $prevYear = $currentYear;
  130. $prevQuarter = $quarter - 1;
  131. }
  132. $startMonth = ($prevQuarter - 1) * 3 + 1;
  133. $endMonth = $prevQuarter * 3;
  134. return [
  135. 'start_date' => "$prevYear-" . str_pad($startMonth, 2, '0', STR_PAD_LEFT) . "-01",
  136. 'end_date' => "$prevYear-" . str_pad($endMonth, 2, '0', STR_PAD_LEFT) . "-" . date('t', strtotime("$prevYear-$endMonth-01"))
  137. ];
  138. case 'year':
  139. return [
  140. 'start_date' => ($currentYear - 1) . "-01-01",
  141. 'end_date' => ($currentYear - 1) . "-12-31"
  142. ];
  143. case 'current':
  144. default:
  145. return [
  146. 'start_date' => (date('Y') - 1) . "-01-01",
  147. 'end_date' => (date('Y') - 1) . "-12-31"
  148. ];
  149. }
  150. }
  151. function calculateVATBreakdown($entries) {
  152. $vatData = [
  153. '0' => [
  154. 'taxable_sales' => 0,
  155. 'taxable_purchases' => 0,
  156. 'payable_vat' => 0,
  157. 'deductible_vat' => 0,
  158. 'net_vat' => 0
  159. ],
  160. '10' => [
  161. 'taxable_sales' => 0,
  162. 'taxable_purchases' => 0,
  163. 'payable_vat' => 0,
  164. 'deductible_vat' => 0,
  165. 'net_vat' => 0
  166. ],
  167. '14' => [
  168. 'taxable_sales' => 0,
  169. 'taxable_purchases' => 0,
  170. 'payable_vat' => 0,
  171. 'deductible_vat' => 0,
  172. 'net_vat' => 0
  173. ],
  174. '24' => [
  175. 'taxable_sales' => 0,
  176. 'taxable_purchases' => 0,
  177. 'payable_vat' => 0,
  178. 'deductible_vat' => 0,
  179. 'net_vat' => 0
  180. ],
  181. '25.5' => [
  182. 'taxable_sales' => 0,
  183. 'taxable_purchases' => 0,
  184. 'payable_vat' => 0,
  185. 'deductible_vat' => 0,
  186. 'net_vat' => 0
  187. ]
  188. ];
  189. foreach ($entries as $entry) {
  190. $vatRate = floatval($entry['vat_percentage']);
  191. $vatKey = normalizeVatRate($vatRate);
  192. if (!isset($vatData[$vatKey])) {
  193. $vatData[$vatKey] = [
  194. 'taxable_sales' => 0,
  195. 'taxable_purchases' => 0,
  196. 'payable_vat' => 0,
  197. 'deductible_vat' => 0,
  198. 'net_vat' => 0
  199. ];
  200. }
  201. $taxFreeAmount = floatval($entry['tax_free_amount']);
  202. $vatAmount = floatval($entry['vat_amount']);
  203. if ($entry['entry_type'] === 'Tulo') {
  204. // Sales - VAT is payable
  205. $vatData[$vatKey]['taxable_sales'] += $taxFreeAmount;
  206. $vatData[$vatKey]['payable_vat'] += $vatAmount;
  207. } else {
  208. // Purchases - VAT is deductible
  209. $vatData[$vatKey]['taxable_purchases'] += $taxFreeAmount;
  210. $vatData[$vatKey]['deductible_vat'] += $vatAmount;
  211. }
  212. }
  213. // Calculate net VAT for each rate
  214. foreach ($vatData as $rate => &$data) {
  215. $data['net_vat'] = $data['payable_vat'] - $data['deductible_vat'];
  216. }
  217. return $vatData;
  218. }
  219. function normalizeVatRate($rate) {
  220. // Normalize VAT rates to standard keys
  221. if ($rate == 0) return '0';
  222. if ($rate == 10) return '10';
  223. if ($rate == 14) return '14';
  224. if ($rate == 24) return '24';
  225. if ($rate == 25.5) return '25.5';
  226. // Handle edge cases
  227. if ($rate > 24 && $rate <= 26) return '25.5';
  228. if ($rate > 14 && $rate <= 15) return '14';
  229. if ($rate > 9 && $rate <= 11) return '10';
  230. return '0'; // Default to 0% if unknown
  231. }
  232. function calculateVATTotals($vatBreakdown) {
  233. $totals = [
  234. 'taxable_sales' => 0,
  235. 'taxable_purchases' => 0,
  236. 'payable_vat' => 0,
  237. 'deductible_vat' => 0,
  238. 'net_vat' => 0
  239. ];
  240. foreach ($vatBreakdown as $data) {
  241. $totals['taxable_sales'] += $data['taxable_sales'];
  242. $totals['taxable_purchases'] += $data['taxable_purchases'];
  243. $totals['payable_vat'] += $data['payable_vat'];
  244. $totals['deductible_vat'] += $data['deductible_vat'];
  245. $totals['net_vat'] += $data['net_vat'];
  246. }
  247. return $totals;
  248. }
  249. function getPreviousPeriodVATData($conn, $dateConditions) {
  250. $sql = "SELECT
  251. ae.*,
  252. ac.category_name,
  253. ac.category_type,
  254. ac.vat_percentage as category_vat
  255. FROM accounting_entries ae
  256. LEFT JOIN accounting_categories ac ON ae.category = ac.category_code
  257. WHERE ae.entry_date BETWEEN :start_date AND :end_date
  258. ORDER BY ae.entry_date";
  259. $stmt = $conn->prepare($sql);
  260. $stmt->bindParam(':start_date', $dateConditions['start_date']);
  261. $stmt->bindParam(':end_date', $dateConditions['end_date']);
  262. $stmt->execute();
  263. $entries = $stmt->fetchAll(PDO::FETCH_ASSOC);
  264. return calculateVATBreakdown($entries);
  265. }
  266. function generateMonthlyVATSummary($conn, $dateConditions) {
  267. $monthlySummary = [];
  268. // Get all months within the date range
  269. $start = new DateTime($dateConditions['start_date']);
  270. $end = new DateTime($dateConditions['end_date']);
  271. $interval = new DateInterval('P1M');
  272. $period = new DatePeriod($start, $interval, $end->modify('last day of this month'));
  273. foreach ($period as $month) {
  274. $monthStart = $month->format('Y-m-01');
  275. $monthEnd = $month->format('Y-m-t');
  276. // Fetch accounting entries for this month
  277. $sql = "SELECT
  278. ae.*,
  279. ac.category_name,
  280. ac.category_type,
  281. ac.vat_percentage as category_vat
  282. FROM accounting_entries ae
  283. LEFT JOIN accounting_categories ac ON ae.category = ac.category_code
  284. WHERE ae.entry_date BETWEEN :start_date AND :end_date
  285. ORDER BY ae.entry_date";
  286. $stmt = $conn->prepare($sql);
  287. $stmt->bindParam(':start_date', $monthStart);
  288. $stmt->bindParam(':end_date', $monthEnd);
  289. $stmt->execute();
  290. $entries = $stmt->fetchAll(PDO::FETCH_ASSOC);
  291. $vatBreakdown = calculateVATBreakdown($entries);
  292. $totals = calculateVATTotals($vatBreakdown);
  293. // Finnish month names
  294. $finnishMonths = [
  295. 'January' => 'Tammikuu',
  296. 'February' => 'Helmikuu',
  297. 'March' => 'Maaliskuu',
  298. 'April' => 'Huhtikuu',
  299. 'May' => 'Toukokuu',
  300. 'June' => 'Kesäkuu',
  301. 'July' => 'Heinäkuu',
  302. 'August' => 'Elokuu',
  303. 'September' => 'Syyskuu',
  304. 'October' => 'Lokakuu',
  305. 'November' => 'Marraskuu',
  306. 'December' => 'Joulukuu'
  307. ];
  308. $englishMonthName = $month->format('F');
  309. $finnishMonthName = $finnishMonths[$englishMonthName] ?? $englishMonthName;
  310. $monthlySummary[] = [
  311. 'month' => $month->format('Y-m'),
  312. 'month_name' => $finnishMonthName . ' ' . $month->format('Y'),
  313. 'payable' => $totals['payable_vat'] ?? 0,
  314. 'deductible' => $totals['deductible_vat'] ?? 0,
  315. 'net' => $totals['net_vat'] ?? 0,
  316. 'turnover' => $totals['taxable_sales'] ?? 0,
  317. 'vat_breakdown' => $vatBreakdown
  318. ];
  319. }
  320. return $monthlySummary;
  321. }
  322. ?>