diff --git a/flujo_de_caja.php b/flujo_de_caja.php index 61e7522..a52a9e5 100644 --- a/flujo_de_caja.php +++ b/flujo_de_caja.php @@ -9,6 +9,13 @@ $start_date = "$year-$month-01"; $days_in_month = cal_days_in_month(CAL_GREGORIAN, $month, $year); $end_date = "$year-$month-$days_in_month"; +$columns = [ + 'bcp_yape', 'b_nacion', 'interbank', 'bbva', 'otros_ingresos', + 'tu1', 'tu2', 'tu3', 'fl1', 'fl2', 'fl3', + 'rc_envio', 'rc_contraent' +]; + +// Fetch data from DB $flujo_data = []; try { $pdo = db(); @@ -22,22 +29,51 @@ try { // Handle error } -$columns = [ - 'bcp_yape', 'b_nacion', 'interbank', 'bbva', 'otros_ingresos', - 'tu1', 'tu2', 'tu3', 'fl1', 'fl2', 'fl3', - 'rc_envio', 'rc_contraent' -]; +// Prepare data for the entire month +$all_days_data = []; +for ($day = 1; $day <= $days_in_month; $day++) { + $date = date('Y-m-d', strtotime("$year-$month-$day")); + $all_days_data[$date] = isset($flujo_data[$date]) ? $flujo_data[$date] : array_fill_keys($columns, '0.00'); +} + +// Calculate totals +$totals = array_fill_keys($columns, 0); +$totals['total_ingresos'] = 0; +$totals['total_inversion'] = 0; +$totals['recaudo_final'] = 0; + +foreach ($all_days_data as $day_data) { + $ingresos_dia = (float)$day_data['bcp_yape'] + (float)$day_data['b_nacion'] + (float)$day_data['interbank'] + (float)$day_data['bbva'] + (float)$day_data['otros_ingresos'] + (float)$day_data['rc_envio'] + (float)$day_data['rc_contraent']; + $inversion_dia = (float)$day_data['tu1'] + (float)$day_data['tu2'] + (float)$day_data['tu3'] + (float)$day_data['fl1'] + (float)$day_data['fl2'] + (float)$day_data['fl3']; + + foreach ($columns as $col) { + $totals[$col] += (float)$day_data[$col]; + } + $totals['total_ingresos'] += $ingresos_dia; + $totals['total_inversion'] += $inversion_dia; + $totals['recaudo_final'] += ($ingresos_dia - $inversion_dia); +} ?> @@ -67,7 +103,7 @@ $columns = [ -
+
@@ -95,21 +131,29 @@ $columns = [ - + $day_data): ?> - + - + + + + + + + + + + + +
0.00 0.00 0.00
TOTAL
@@ -118,17 +162,14 @@ $columns = [ document.addEventListener('DOMContentLoaded', function() { const table = document.querySelector('.table'); - // Function to update totals for a specific row function updateTotals(row) { const getVal = (selector) => { const cell = row.querySelector(`[data-column="${selector}"]`); - return parseFloat(cell.textContent) || 0; + return parseFloat(cell.textContent.replace(/,/g, '')) || 0; }; const ingresos = getVal('bcp_yape') + getVal('b_nacion') + getVal('interbank') + getVal('bbva') + getVal('otros_ingresos') + getVal('rc_envio') + getVal('rc_contraent'); - const totalInversion = getVal('tu1') + getVal('tu2') + getVal('tu3') + getVal('fl1') + getVal('fl2') + getVal('fl3'); - const recaudoFinal = ingresos - totalInversion; row.querySelector('.total-ingresos').textContent = ingresos.toFixed(2); @@ -136,10 +177,34 @@ document.addEventListener('DOMContentLoaded', function() { row.querySelector('.recaudo-final').textContent = recaudoFinal.toFixed(2); } - // Initial calculation for all rows - table.querySelectorAll('tbody tr').forEach(updateTotals); + function updateGrandTotals() { + const grandTotals = { + + 'total-ingresos': 0, + 'total-inversion': 0, + 'recaudo-final': 0 + }; + + table.querySelectorAll('tbody tr').forEach(row => { + + grandTotals[''] += parseFloat(row.querySelector(`[data-column=""]`).textContent.replace(/,/g, '')) || 0; + + grandTotals['total-ingresos'] += parseFloat(row.querySelector('.total-ingresos').textContent.replace(/,/g, '')) || 0; + grandTotals['total-inversion'] += parseFloat(row.querySelector('.total-inversion').textContent.replace(/,/g, '')) || 0; + grandTotals['recaudo-final'] += parseFloat(row.querySelector('.recaudo-final').textContent.replace(/,/g, '')) || 0; + }); + + for (const key in grandTotals) { + const th = table.querySelector(`tfoot [data-total-column="${key}"]`); + if (th) { + th.textContent = grandTotals[key].toFixed(2); + } + } + } + + table.querySelectorAll('tbody tr').forEach(updateTotals); + updateGrandTotals(); - // Event listener for cell focus to select all content table.addEventListener('focus', function(e) { if (e.target.hasAttribute('contenteditable')) { const range = document.createRange(); @@ -148,50 +213,43 @@ document.addEventListener('DOMContentLoaded', function() { sel.removeAllRanges(); sel.addRange(range); } - }, true); // Use capturing for focus + }, true); - // Event listener for cell edits table.addEventListener('blur', function(e) { if (e.target.hasAttribute('contenteditable')) { const cell = e.target; const row = cell.closest('tr'); const date = row.dataset.date; const column = cell.dataset.column; - let value = parseFloat(cell.textContent); + let value = parseFloat(cell.textContent.replace(/,/g, '')); if (isNaN(value)) { value = 0; - cell.textContent = '0.00'; } cell.textContent = value.toFixed(2); - // Save data to server fetch('save_flujo_caja.php', { method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ fecha: date, columna: column, valor: value }) }) .then(response => response.json()) .then(data => { - if (!data.success) { + if (data.success) { + updateTotals(row); + updateGrandTotals(); // Update grand totals after successful save + } else { console.error('Error saving data:', data.message); - // Optional: Add user feedback for save failure } }) .catch(error => console.error('Fetch error:', error)); - - // Update totals for the edited row - updateTotals(row); } - }, true); // Use capturing to ensure blur event is handled properly + }, true); - // Event listener for Enter key to save and not expand table.addEventListener('keydown', function(e) { if (e.key === 'Enter' && e.target.hasAttribute('contenteditable')) { - e.preventDefault(); // Prevent new line - e.target.blur(); // Trigger blur to save + e.preventDefault(); + e.target.blur(); } }); }); @@ -199,4 +257,4 @@ document.addEventListener('DOMContentLoaded', function() { +?> \ No newline at end of file diff --git a/layout_header.php b/layout_header.php index aa3e9f5..036995e 100644 --- a/layout_header.php +++ b/layout_header.php @@ -76,7 +76,7 @@ $navItems = [ ], 'inventario_group' => [ 'icon' => 'fa-warehouse', - 'text' => 'Inventario', + 'text' => 'Inventario General', 'roles' => ['Administrador', 'admin', 'Control Logistico'], 'submenu' => [ 'panel_inventario' => [ diff --git a/panel_inventario.php b/panel_inventario.php index 81c500a..d1b0bc0 100644 --- a/panel_inventario.php +++ b/panel_inventario.php @@ -9,51 +9,160 @@ require_once 'db/config.php'; try { $pdo = db(); - $stmt = $pdo->query("SELECT * FROM products ORDER BY order_position ASC"); - $products = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // 1. Datos para las tarjetas de resumen + $total_productos = $pdo->query("SELECT COUNT(*) FROM products")->fetchColumn(); + $total_sedes = $pdo->query("SELECT COUNT(*) FROM sedes")->fetchColumn(); + $total_stock = $pdo->query("SELECT SUM(quantity) FROM stock_sedes")->fetchColumn(); + + // 2. Datos para la tabla de inventario + $sedes_stmt = $pdo->query("SELECT id, nombre FROM sedes ORDER BY nombre"); + $sedes = $sedes_stmt->fetchAll(PDO::FETCH_ASSOC); + + $products_stmt = $pdo->query("SELECT id, nombre FROM products ORDER BY nombre"); + $products = $products_stmt->fetchAll(PDO::FETCH_ASSOC); + + $stock_stmt = $pdo->query("SELECT product_id, sede_id, quantity FROM stock_sedes"); + $stock_data = $stock_stmt->fetchAll(PDO::FETCH_ASSOC); + + // Procesar datos para la tabla + $inventario = []; + foreach ($products as $product) { + $inventario[$product['id']] = [ + 'nombre' => $product['nombre'], + 'total' => 0, + 'sedes' => array_fill_keys(array_column($sedes, 'id'), 0) + ]; + } + + foreach ($stock_data as $stock_item) { + if (isset($inventario[$stock_item['product_id']])) { + $inventario[$stock_item['product_id']]['sedes'][$stock_item['sede_id']] = $stock_item['quantity']; + $inventario[$stock_item['product_id']]['total'] += $stock_item['quantity']; + } + } + + // 3. Datos para el gráfico circular (Distribución por Sede) + $stock_por_sede_stmt = $pdo->query(" + SELECT s.nombre, SUM(ss.quantity) as total_stock + FROM sedes s + JOIN stock_sedes ss ON s.id = ss.sede_id + GROUP BY s.nombre + HAVING total_stock > 0 + ORDER BY s.nombre + "); + $stock_por_sede = $stock_por_sede_stmt->fetchAll(PDO::FETCH_ASSOC); + $sede_chart_labels = json_encode(array_column($stock_por_sede, 'nombre')); + $sede_chart_data = json_encode(array_column($stock_por_sede, 'total_stock')); + + // 4. Datos para el gráfico de barras (Stock por Producto) + $stock_por_producto_stmt = $pdo->query(" + SELECT p.nombre, SUM(ss.quantity) as total_stock + FROM products p + JOIN stock_sedes ss ON p.id = ss.product_id + GROUP BY p.nombre + HAVING total_stock > 0 + ORDER BY total_stock DESC + LIMIT 15 + "); + $stock_por_producto = $stock_por_producto_stmt->fetchAll(PDO::FETCH_ASSOC); + $producto_chart_labels = json_encode(array_column($stock_por_producto, 'nombre')); + $producto_chart_data = json_encode(array_column($stock_por_producto, 'total_stock')); + + } catch (PDOException $e) { - echo "
Error al conectar con la base de datos: " . $e->getMessage() . "
"; - // Consider logging the error and showing a more user-friendly message - // For now, we stop execution if the database connection fails. + echo "
Error al conectar o consultar la base de datos: " . $e->getMessage() . "
"; + require_once 'layout_footer.php'; die(); } ?>
-
-

Inventario General

- Añadir Producto +

Dashboard de Inventario

+ + +
+
+
+
+
Total de Productos
+

+
+
+
+
+
+
+
Sedes Activas
+

+
+
+
+
+
+
+
Unidades Totales en Stock
+

+
+
+
+ +
+
+
+
+
Distribución por Sede
+
+
+ +
+
+
+
+
+
+
Top 15 Productos con Más Stock
+
+
+ +
+
+
+
+ + +
+
+
Inventario por Sede
+
- - +
+ - - - - - + + + + + - + - + - + - - - - - + + + + + @@ -64,4 +173,88 @@ try { + + + + + \ No newline at end of file
IDNombreSKUPrecioAccionesProductoStock Total
No hay productos en el inventario. Agrega el primero.No hay productos para mostrar.
S/ - Editar - Eliminar -