Autosave: 20260203-060838

This commit is contained in:
Flatlogic Bot 2026-02-03 06:08:39 +00:00
parent 2caed5f3df
commit 96d2546dba
3 changed files with 268 additions and 6 deletions

View File

@ -25,18 +25,51 @@ try {
$columns = [
'bcp_yape', 'b_nacion', 'interbank', 'bbva', 'otros_ingresos',
'tu1', 'tu2', 'tu3', 'fl1', 'fl2', 'fl3',
'rc_envio', 'rc_contraent', 'total_inversion_publicitaria'
'rc_envio', 'rc_contraent'
];
?>
<style>
.table thead th {
background-color: #e9ecef; /* Un gris claro y profesional */
color: #495057;
font-weight: 600;
}
.table td[contenteditable="true"]:focus {
background-color: #fff3cd; /* Un amarillo suave para resaltar la celda activa */
}
</style>
<div class="container-fluid mt-4">
<h2>Flujo de Caja</h2>
<p>Registro y control de los movimientos de ingresos y gastos.</p>
<form method="GET" action="flujo_de_caja.php" class="form-inline mb-4">
<div class="form-group mr-2">
<label for="month" class="mr-2">Mes:</label>
<select name="month" id="month" class="form-control">
<?php for ($m = 1; $m <= 12; $m++): ?>
<option value="<?php echo str_pad($m, 2, '0', STR_PAD_LEFT); ?>" <?php echo $m == $month ? 'selected' : ''; ?>>
<?php echo DateTime::createFromFormat('!m', $m)->format('F'); ?>
</option>
<?php endfor; ?>
</select>
</div>
<div class="form-group mr-2">
<label for="year" class="mr-2">Año:</label>
<select name="year" id="year" class="form-control">
<?php for ($y = date('Y'); $y >= date('Y') - 5; $y--): ?>
<option value="<?php echo $y; ?>" <?php echo $y == $year ? 'selected' : ''; ?>><?php echo $y; ?></option>
<?php endfor; ?>
</select>
</div>
<button type="submit" class="btn btn-primary">Filtrar</button>
</form>
<div class="table-responsive">
<table class="table table-bordered table-striped table-hover" style="width: 100%;">
<thead class="thead-dark">
<thead>
<tr>
<th rowspan="2" style="vertical-align: middle; text-align: center;">Fecha</th>
<th colspan="5" style="text-align: center;">Ingresos</th>
@ -93,9 +126,8 @@ document.addEventListener('DOMContentLoaded', function() {
};
const ingresos = getVal('bcp_yape') + getVal('b_nacion') + getVal('interbank') + getVal('bbva') + getVal('otros_ingresos') + getVal('rc_envio') + getVal('rc_contraent');
const inversionManual = getVal('total_inversion_publicitaria');
const inversionAuto = getVal('tu1') + getVal('tu2') + getVal('tu3') + getVal('fl1') + getVal('fl2') + getVal('fl3');
const totalInversion = inversionManual > 0 ? inversionManual : inversionAuto;
const totalInversion = getVal('tu1') + getVal('tu2') + getVal('tu3') + getVal('fl1') + getVal('fl2') + getVal('fl3');
const recaudoFinal = ingresos - totalInversion;
@ -107,6 +139,17 @@ document.addEventListener('DOMContentLoaded', function() {
// Initial calculation for all rows
table.querySelectorAll('tbody tr').forEach(updateTotals);
// Event listener for cell focus to select all content
table.addEventListener('focus', function(e) {
if (e.target.hasAttribute('contenteditable')) {
const range = document.createRange();
range.selectNodeContents(e.target);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
}, true); // Use capturing for focus
// Event listener for cell edits
table.addEventListener('blur', function(e) {
if (e.target.hasAttribute('contenteditable')) {
@ -143,9 +186,17 @@ document.addEventListener('DOMContentLoaded', function() {
updateTotals(row);
}
}, true); // Use capturing to ensure blur event is handled properly
// 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
}
});
});
</script>
<?php
require_once 'layout_footer.php';
?>
?>

View File

@ -80,6 +80,37 @@ $navItems = [
'text' => 'Gestionar Productos',
'roles' => ['Administrador', 'admin']
],
'panel_inventario' => [
'icon' => 'fa-warehouse',
'text' => 'Panel de Inventario',
'roles' => ['Administrador', 'admin', 'Control Logistico'],
'submenu' => [
'dashboard' => [
'url' => 'panel_inventario.php?seccion=dashboard',
'icon' => 'fa-tachometer-alt',
'text' => 'Dashboard',
'roles' => ['Administrador', 'admin', 'Control Logistico']
],
'entrada' => [
'url' => 'panel_inventario.php?seccion=entrada',
'icon' => 'fa-arrow-circle-down',
'text' => 'Registro de Entrada',
'roles' => ['Administrador', 'admin', 'Control Logistico']
],
'salida' => [
'url' => 'panel_inventario.php?seccion=salida',
'icon' => 'fa-arrow-circle-up',
'text' => 'Registro de Salida',
'roles' => ['Administrador', 'admin', 'Control Logistico']
],
'registro_producto' => [
'url' => 'panel_inventario.php?seccion=registro_producto',
'icon' => 'fa-plus-circle',
'text' => 'Registro de Producto',
'roles' => ['Administrador', 'admin', 'Control Logistico']
]
]
],
'finanzas_group' => [
'icon' => 'fa-dollar-sign',
'text' => 'Finanzas',

180
panel_inventario.php Normal file
View File

@ -0,0 +1,180 @@
<?php
$pageTitle = "Panel de Inventario";
require_once 'layout_header.php';
require_once 'db/config.php';
// Determine the current section from the URL, default to 'dashboard'
$seccion = isset($_GET['seccion']) ? $_GET['seccion'] : 'dashboard';
$message = '';
$message_type = '';
// Handle product registration form submission
if ($seccion === 'registro_producto' && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['nombre_producto'])) {
$nombre_producto = trim($_POST['nombre_producto']);
if (!empty($nombre_producto)) {
try {
$pdo = db();
// Check if product already exists
$stmt = $pdo->prepare("SELECT COUNT(*) FROM products WHERE nombre = ?");
$stmt->execute([$nombre_producto]);
if ($stmt->fetchColumn() > 0) {
$message = "Error: El producto '{$nombre_producto}' ya existe.";
$message_type = "danger";
} else {
// Insert new product with default values
$stmt = $pdo->prepare("INSERT INTO products (nombre, unidades_disponibles, cobertura, show_on_panel, order_position) VALUES (?, 0, '[]', 1, 0)");
if ($stmt->execute([$nombre_producto])) {
$message = "Producto '{$nombre_producto}' registrado con éxito.";
$message_type = "success";
} else {
$message = "Error al registrar el producto.";
$message_type = "danger";
}
}
} catch (PDOException $e) {
$message = "Error de base de datos: " . $e->getMessage();
$message_type = "danger";
}
} else {
$message = "El nombre del producto no puede estar vacío.";
$message_type = "warning";
}
}
// Set the title based on the section
switch ($seccion) {
case 'entrada':
$pageTitle = "Registro de Entrada";
break;
case 'salida':
$pageTitle = "Registro de Salida";
break;
case 'registro_producto':
$pageTitle = "Registro de Nuevo Producto";
break;
case 'dashboard':
default:
$pageTitle = "Dashboard de Inventario";
break;
}
?>
<div class="container mt-4">
<?php if ($message): ?>
<div class="alert alert-<?php echo $message_type; ?> alert-dismissible fade show" role="alert">
<?php echo htmlspecialchars($message); ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<?php
// Display content based on the selected section
if ($seccion === 'entrada'):
?>
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Registro de Entrada de Inventario</h5>
</div>
<div class="card-body">
<p>Esta sección está en construcción. Aquí podrás registrar la entrada de nuevo stock.</p>
</div>
</div>
<?php
elseif ($seccion === 'salida'):
?>
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Registro de Salida de Inventario</h5>
</div>
<div class="card-body">
<p>Esta sección está en construcción. Aquí podrás registrar la salida de stock por ventas u otros motivos.</p>
</div>
</div>
<?php
elseif ($seccion === 'registro_producto'):
?>
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Registrar Nuevo Producto</h5>
</div>
<div class="card-body">
<form method="POST" action="panel_inventario.php?seccion=registro_producto">
<div class="mb-3">
<label for="nombre_producto" class="form-label">Nombre del Producto</label>
<input type="text" class="form-control" id="nombre_producto" name="nombre_producto" required>
</div>
<button type="submit" class="btn btn-primary">Guardar Producto</button>
</form>
</div>
</div>
<?php
else: // Default to dashboard
// Fetch product data from the database
try {
$pdo = db();
$stmt = $pdo->query("SELECT nombre, unidades_disponibles, cobertura FROM products ORDER BY nombre ASC");
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
echo "<div class='alert alert-danger'>Error al conectar con la base de datos: " . $e->getMessage() . "</div>";
$products = [];
}
?>
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Stock de Productos por Ciudad</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="thead-light">
<tr>
<th scope="col">Producto</th>
<th scope="col" class="text-center">Stock Total</th>
<th scope="col">Ciudades de Cobertura</th>
</tr>
</thead>
<tbody>
<?php if (empty($products)): ?>
<tr>
<td colspan="3" class="text-center">No hay productos para mostrar.</td>
</tr>
<?php else: ?>
<?php foreach ($products as $product): ?>
<tr>
<td><?php echo htmlspecialchars($product['nombre']); ?></td>
<td class="text-center"><?php echo htmlspecialchars($product['unidades_disponibles']); ?></td>
<td>
<?php
$cobertura_list = json_decode($product['cobertura'], true);
if (json_last_error() === JSON_ERROR_NONE && is_array($cobertura_list)) {
echo htmlspecialchars(implode(', ', $cobertura_list));
} else {
echo htmlspecialchars($product['cobertura']);
}
?>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php
endif;
?>
</div>
<?php
// We need to re-include the header to update the title dynamically
if (isset($pageTitle)) {
echo "<script>document.title = '" . htmlspecialchars($pageTitle) . "';</script>";
echo "<script>document.querySelector('.content h1').textContent = '" . htmlspecialchars($pageTitle) . "';</script>";
}
require_once 'layout_footer.php';
?>