diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index cb8327e..9a038af 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -23,6 +23,31 @@ class ReportController extends Controller ->groupBy('expense_category') ->get(); + // 2.5 Ingresos por Categoría (Para Pie Chart) + $rawEarningsByCategory = \App\Models\SaleDetail::join('products', 'sale_details.product_id', '=', 'products.id') + ->select('products.type', DB::raw('SUM(sale_details.quantity * sale_details.price) as total_earnings')) + ->groupBy('products.type') + ->get(); + + $categoryTranslations = [ + 'bike' => 'Bicicletas', + 'clothing' => 'Indumentaria', + 'accessory' => 'Accesorios', + 'spare' => 'Repuestos', + 'children' => 'Niños', + 'rollers' => 'Rollers', + 'skate' => 'Skates', + 'other' => 'Otros' + ]; + + $earningsByCategory = $rawEarningsByCategory->map(function($item) use ($categoryTranslations) { + $type = $item->type; + return [ + 'category' => $categoryTranslations[$type] ?? ucfirst($type), + 'total' => $item->total_earnings + ]; + }); + // 3. Ingresos y Egresos por Mes (Últimos 6 meses) $months = []; $monthlyIncomeData = []; @@ -81,6 +106,7 @@ class ReportController extends Controller 'totalOutcome', 'balance', 'expensesByCategory', + 'earningsByCategory', 'months', 'monthlyIncomeData', 'monthlyOutcomeData', diff --git a/resources/views/reports/index.blade.php b/resources/views/reports/index.blade.php index 49982ed..edc5f11 100644 --- a/resources/views/reports/index.blade.php +++ b/resources/views/reports/index.blade.php @@ -50,19 +50,27 @@ - + +
+

Evolución de Ingresos y Egresos (Últimos 6 meses)

+
+ +
+
+ +
- +
-

Evolución de Ingresos y Egresos (Últimos 6 meses)

-
- +

Ingresos por Categoría

+
+
- +
-

Distribución de Gastos por Categoría

+

Gastos por Categoría

@@ -211,6 +219,49 @@ } }); + // --- GRÁFICO DE INGRESOS POR CATEGORÍA (Torta) --- + const ctxEarnings = document.getElementById('earningsChart').getContext('2d'); + + const rawEarnings = @json($earningsByCategory); + const earnLabels = rawEarnings.map(item => item.category); + const earnData = rawEarnings.map(item => item.total); + + // Paleta de colores distintiva (verdes/limas/turquesas) + const earnColors = [ + '#84cc16', // neon-lime / lime-500 + '#14b8a6', // teal-500 + '#06b6d4', // cyan-500 + '#6366f1', // indigo-500 + '#10b981', // emerald-500 + '#eab308', // yellow-500 + '#f97316', // orange-500 + '#8b5cf6' // violet-500 + ]; + + new Chart(ctxEarnings, { + type: 'doughnut', + data: { + labels: earnLabels, + datasets: [{ + data: earnData, + backgroundColor: earnColors, + borderWidth: 2, + borderColor: isDark ? '#1f2937' : '#ffffff' + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'right', + labels: { color: textColor, padding: 20 } + } + }, + cutout: '60%' + } + }); + // --- GRÁFICO DE TOP PRODUCTOS --- const ctxTopProducts = document.getElementById('topProductsChart').getContext('2d'); const rawTopProducts = @json($topProducts);