diff --git a/dashboard.php b/dashboard.php index 78963ed..8a6c6bf 100755 --- a/dashboard.php +++ b/dashboard.php @@ -533,6 +533,8 @@ switch ($page) { $balance = Report::getGeneralBalance(); $expensesByCategory = Report::getExpensesByCategory(); $accessibleHouseIds = Auth::getAccessibleHouseIds(); + $filterYear = $_GET['filter_year'] ?? null; + $conceptDetails = Report::getConceptDetailsByYear($filterYear); if ($reportType == 'water-debtors') { $filters = [ @@ -754,6 +756,8 @@ switch ($page) { // Requerimos el modelo Report require_once __DIR__ . '/models/Report.php'; $balance = Report::getGeneralBalance(); + $filterYear = $_GET['filter_year'] ?? null; + $conceptDetails = Report::getConceptDetailsByYear($filterYear); include __DIR__ . '/views/reports/pdf_balance.php'; break; } @@ -763,25 +767,63 @@ switch ($page) { exit; case 'export_csv_balance': - // Lógica para exportar CSV de balance - header('Content-Type: text/csv'); - header('Content-Disposition: attachment; filename="Balance_General_IBIZA_' . date('Y') . '.csv"'); - fputs(fopen('php://output', 'w'), $bom = (chr(0xEF) . chr(0xBB) . chr(0xBF))); // BOM for UTF-8 in Excel - - // Requerimos el modelo Report + Auth::requireAdmin(); require_once __DIR__ . '/models/Report.php'; + + $filterYear = $_GET['filter_year'] ?? null; $balance = Report::getGeneralBalance(); + $conceptDetails = Report::getConceptDetailsByYear($filterYear); + + header('Content-Type: text/csv'); + $filename = 'Balance_General_IBIZA'; + if ($filterYear) { + $filename .= '_' . $filterYear; + } + header('Content-Disposition: attachment; filename="' . $filename . '.csv"'); + fputs(fopen('php://output', 'w'), $bom = (chr(0xEF) . chr(0xBB) . chr(0xBF))); $output = fopen('php://output', 'w'); - fputcsv($output, ['Condominio IBIZA-Cto Sierra Morena 152 - Balance General ' . date('Y')]); + fputcsv($output, ['Condominio IBIZA-Cto Sierra Morena 152 - Balance General']); + if ($filterYear) { + fputcsv($output, ['Año: ' . $filterYear]); + } fputcsv($output, ['']); // Blank line fputcsv($output, ['Descripción', 'Monto']); fputcsv($output, ['Total Ingresos (Conceptos)', $balance['total_incomes']]); - if (!Auth::isLector()) { - fputcsv($output, ['Total Egresos', $balance['total_expenses']]); - fputcsv($output, ['Balance Neto', $balance['balance']]); + fputcsv($output, ['Total Egresos', $balance['total_expenses']]); + fputcsv($output, ['Balance Neto', $balance['balance']]); + fputcsv($output, ['']); // Blank line + + if (!empty($conceptDetails['concepts'])) { + fputcsv($output, ['Detalle de Conceptos Especiales']); + fputcsv($output, ['']); // Blank line + fputcsv($output, ['Concepto', 'Descripción', 'Esperado', 'Recaudado', 'Pendiente', 'Gastos', 'Balance']); + + foreach ($conceptDetails['concepts'] as $cd) { + fputcsv($output, [ + $cd['concept']['name'], + $cd['concept']['description'] ?? '-', + $cd['expected'], + $cd['collected'], + $cd['pending'], + $cd['expenses'], + $cd['balance'] + ]); + } + + fputcsv($output, ['']); // Blank line + fputcsv($output, [ + 'TOTALES', + '', + $conceptDetails['totals']['expected'], + $conceptDetails['totals']['collected'], + $conceptDetails['totals']['pending'], + $conceptDetails['totals']['expenses'], + $conceptDetails['totals']['balance'] + ]); } + fclose($output); exit; diff --git a/models/Report.php b/models/Report.php index 1f10d18..2fe07ab 100755 --- a/models/Report.php +++ b/models/Report.php @@ -66,6 +66,92 @@ class Report { ]; } + public static function getConceptDetailsByYear($year = null) { + $db = Database::getInstance(); + $concepts = $db->fetchAll( + "SELECT c.id, c.name, c.amount_per_house, c.concept_date, c.description + FROM finance_collection_concepts c + WHERE c.is_active = 1 + ORDER BY c.concept_date DESC" + ); + + $conceptsData = []; + $totalExpected = 0; + $totalCollected = 0; + $totalExpenses = 0; + + foreach ($concepts as $concept) { + $conceptYear = date('Y', strtotime($concept['concept_date'])); + + if ($year && $conceptYear != $year) { + continue; + } + + $activeHousesCount = $db->fetchOne( + "SELECT COUNT(*) as count FROM houses WHERE status = 'activa'" + ); + $activeHousesCount = $activeHousesCount['count'] ?? 0; + + $expected = $concept['amount_per_house'] * $activeHousesCount; + + $whereClause = "WHERE cp.concept_id = ?"; + $params = [$concept['id']]; + + if ($year) { + $whereClause .= " AND YEAR(cp.payment_date) = ?"; + $params[] = $year; + } + + $collected = $db->fetchOne( + "SELECT COALESCE(SUM(cp.amount), 0) as total + FROM finance_collection_payments cp + $whereClause", + $params + ); + $collectedAmount = $collected['total'] ?? 0; + + $expenses = $db->fetchOne( + "SELECT COALESCE(SUM(ec.amount), 0) as total + FROM expense_concept_allocations ec + JOIN expenses e ON ec.expense_id = e.id + WHERE ec.concept_id = ? + " . ($year ? "AND YEAR(e.expense_date) = ?" : ""), + $year ? [$concept['id'], $year] : [$concept['id']] + ); + $expensesAmount = $expenses['total'] ?? 0; + + $balance = $collectedAmount - $expensesAmount; + + $conceptsData[] = [ + 'concept' => $concept, + 'year' => $conceptYear, + 'expected' => $expected, + 'collected' => $collectedAmount, + 'expenses' => $expensesAmount, + 'balance' => $balance, + 'pending' => max(0, $expected - $collectedAmount) + ]; + + $totalExpected += $expected; + $totalCollected += $collectedAmount; + $totalExpenses += $expensesAmount; + } + + $totalBalance = $totalCollected - $totalExpenses; + + return [ + 'concepts' => $conceptsData, + 'totals' => [ + 'expected' => $totalExpected, + 'collected' => $totalCollected, + 'expenses' => $totalExpenses, + 'balance' => $totalBalance, + 'pending' => max(0, $totalExpected - $totalCollected) + ], + 'year' => $year + ]; + } + public static function getHouseStatement($houseId, $year = null) { $db = Database::getInstance(); $house = House::findById($houseId); diff --git a/views/reports/index.php b/views/reports/index.php index 9752ce4..24b7599 100755 --- a/views/reports/index.php +++ b/views/reports/index.php @@ -521,6 +521,60 @@ function exportConceptsCSV() { +
+
+
+ Filtro por Año +
+ +
+
+
+
+
+
+ + +
+
+
+
+ + + Limpiar Filtro + +
+
+
+
+
+
+ + +
@@ -596,33 +650,75 @@ function exportConceptsCSV() {
-
+
-
Resumen Financiero
+
+ Resumen Financiero + + () + +
- - - - - - - - - - - - - - - -
Total Ingresos (Conceptos):$
Total Egresos:$
Balance: - $ -
+ +

No hay conceptos especiales registrados para el año seleccionado.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ConceptoEsperadoRecaudadoPendienteGastos AsociadosBalance
+ + + + + $$$$ + $ + -
TOTALES:$$$$ + $ + -
+
@@ -666,11 +762,21 @@ function exportConceptsCSV() {