From 1bf602b5b0ff76f244b5c181b055e03f75df1910 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Tue, 17 Feb 2026 03:13:39 +0000 Subject: [PATCH] Autosave: 20260217-031338 --- assets/css/style.css | 21 +- layout_header.php | 92 ++++----- pedidos.php | 2 + registrar_entrada_manual_api.php | 62 ++++++ registrar_salida_manual_api.php | 90 +++++---- registro_entrada.php | 262 +++++++++++++++++-------- registro_salida.php | 320 ++++++++++++++++--------------- 7 files changed, 521 insertions(+), 328 deletions(-) create mode 100644 registrar_entrada_manual_api.php diff --git a/assets/css/style.css b/assets/css/style.css index 0bf4450..4549b98 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -16,11 +16,11 @@ body { left: 0; background: linear-gradient(145deg, #2c3e50, #34495e); color: #ecf0f1; - padding-top: 20px; box-shadow: 2px 0 15px rgba(0,0,0,0.1); transition: transform 0.3s ease; z-index: 1000; - overflow-y: auto; /* Permite el scroll vertical si el contenido es muy largo */ + display: flex; + flex-direction: column; } .sidebar .navbar-brand { @@ -29,6 +29,12 @@ body { font-weight: bold; text-align: center; font-size: 1.5rem; + flex-shrink: 0; /* Prevent brand from shrinking */ +} + +.menu-wrapper { + flex-grow: 1; /* Takes up all available space */ + overflow-y: auto; /* Allows scrolling for menu items */ } .sidebar .nav-link { @@ -53,10 +59,13 @@ body { padding-left: 21px; } -.sidebar .nav-item.mt-auto { - position: absolute; - bottom: 20px; - width: 100%; +.logout-wrapper { + flex-shrink: 0; /* Prevents the logout area from shrinking */ + border-top: 1px solid #4a627a; /* Separator line */ +} + +.logout-wrapper .nav-link { + font-weight: bold; } /* Content Area Styles */ diff --git a/layout_header.php b/layout_header.php index 9a52698..8aec238 100644 --- a/layout_header.php +++ b/layout_header.php @@ -216,7 +216,7 @@ $navItems = [ - <?php echo isset($pageTitle) ? htmlspecialchars($pageTitle) : 'FLOOWER CRM'; ?> + <?php echo isset($pageTitle) ? htmlspecialchars($pageTitle) : 'FLOOWER ERP'; ?> @@ -230,53 +230,55 @@ $navItems = [ +
+ Cerrar Sesión +
-

\ No newline at end of file +

diff --git a/pedidos.php b/pedidos.php index 20c9e35..1b987be 100644 --- a/pedidos.php +++ b/pedidos.php @@ -155,6 +155,7 @@ include 'layout_header.php'; Celular Producto Sede de Envío + Cantidad Monto Total Monto Debe Nº De Orden @@ -176,6 +177,7 @@ include 'layout_header.php'; + diff --git a/registrar_entrada_manual_api.php b/registrar_entrada_manual_api.php new file mode 100644 index 0000000..6cb83df --- /dev/null +++ b/registrar_entrada_manual_api.php @@ -0,0 +1,62 @@ + false, 'message' => 'Petición inválida.']; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $sede_id = isset($_POST['sede_id']) ? filter_var($_POST['sede_id'], FILTER_VALIDATE_INT) : null; + $product_id = isset($_POST['product_id']) ? filter_var($_POST['product_id'], FILTER_VALIDATE_INT) : null; + $cantidad = isset($_POST['cantidad']) ? filter_var($_POST['cantidad'], FILTER_VALIDATE_INT) : null; + + if (!$sede_id || !$product_id || !$cantidad || $cantidad <= 0) { + $response['message'] = 'Datos inválidos. Por favor, complete todos los campos correctamente.'; + echo json_encode($response); + exit; + } + + try { + $pdo = db(); + $pdo->beginTransaction(); + + // 1. Actualizar o insertar en stock_sedes + $sql_stock = "INSERT INTO stock_sedes (id_producto, id_sede, cantidad) + VALUES (:product_id, :sede_id, :cantidad) + ON DUPLICATE KEY UPDATE cantidad = cantidad + :cantidad"; + + $stmt_stock = $pdo->prepare($sql_stock); + $stmt_stock->bindParam(':product_id', $product_id, PDO::PARAM_INT); + $stmt_stock->bindParam(':sede_id', $sede_id, PDO::PARAM_INT); + $stmt_stock->bindParam(':cantidad', $cantidad, PDO::PARAM_INT); + $stmt_stock->execute(); + + // 2. Registrar el movimiento + $sql_movement = "INSERT INTO stock_movements (product_id, sede_id, tipo_movimiento, cantidad, origen) + VALUES (:product_id, :sede_id, 'entrada', :cantidad, 'manual')"; + + $stmt_movement = $pdo->prepare($sql_movement); + $stmt_movement->bindParam(':product_id', $product_id, PDO::PARAM_INT); + $stmt_movement->bindParam(':sede_id', $sede_id, PDO::PARAM_INT); + $stmt_movement->bindParam(':cantidad', $cantidad, PDO::PARAM_INT); + $stmt_movement->execute(); + + $pdo->commit(); + + $response['success'] = true; + $response['message'] = "Se han añadido {$cantidad} unidades al stock correctamente."; + + } catch (PDOException $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + // Log del error para depuración, no mostrar al usuario final + error_log("Error en registrar_entrada_manual_api.php: " . $e->getMessage()); + $response['message'] = 'Error en la base de datos. No se pudo registrar la entrada.'; + } + +} else { + $response['message'] = 'Método no permitido.'; +} + +echo json_encode($response); +?> diff --git a/registrar_salida_manual_api.php b/registrar_salida_manual_api.php index 4c3a106..cc3959c 100644 --- a/registrar_salida_manual_api.php +++ b/registrar_salida_manual_api.php @@ -5,72 +5,70 @@ require_once 'db/config.php'; $response = ['success' => false, 'message' => 'Petición inválida.']; if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $sede_id = isset($_POST['sede_id']) ? trim($_POST['sede_id']) : ''; - $product_id_raw = isset($_POST['product_id']) ? trim($_POST['product_id']) : ''; - $quantity_to_remove = isset($_POST['quantity']) ? (int)$_POST['quantity'] : 0; + $sede_id = isset($_POST['sede_id']) ? filter_var($_POST['sede_id'], FILTER_VALIDATE_INT) : null; + $product_id = isset($_POST['product_id']) ? filter_var($_POST['product_id'], FILTER_VALIDATE_INT) : null; + $quantity = isset($_POST['quantity']) ? filter_var($_POST['quantity'], FILTER_VALIDATE_INT) : null; - // Extraer el ID numérico del producto - $product_id_parts = explode('-', $product_id_raw); - $product_id = end($product_id_parts); - - if (empty($sede_id) || !is_numeric($product_id) || $quantity_to_remove <= 0) { - $response['message'] = 'Por favor, complete todos los campos correctamente.'; + if (!$sede_id || !$product_id || !$quantity || $quantity <= 0) { + $response['message'] = 'Datos inválidos. Por favor, complete todos los campos correctamente.'; echo json_encode($response); exit; } - $pdo = db(); - $pdo->beginTransaction(); - try { - // 1. Obtener el stock actual y bloquear la fila para evitar concurrencia - $stmt = $pdo->prepare("SELECT quantity FROM stock_sedes WHERE sede_id = :sede_id AND product_id = :product_id FOR UPDATE"); - $stmt->execute(['sede_id' => $sede_id, 'product_id' => $product_id]); - $stock = $stmt->fetch(PDO::FETCH_ASSOC); + $pdo = db(); + $pdo->beginTransaction(); - $current_quantity = $stock ? (int)$stock['quantity'] : 0; + // 1. Verificar stock actual + $sql_check = "SELECT cantidad FROM stock_sedes WHERE id_producto = :product_id AND id_sede = :sede_id FOR UPDATE"; + $stmt_check = $pdo->prepare($sql_check); + $stmt_check->bindParam(':product_id', $product_id, PDO::PARAM_INT); + $stmt_check->bindParam(':sede_id', $sede_id, PDO::PARAM_INT); + $stmt_check->execute(); + $current_stock_row = $stmt_check->fetch(PDO::FETCH_ASSOC); - if ($current_quantity < $quantity_to_remove) { - $response['message'] = "No se puede retirar más stock del disponible. Stock actual: {$current_quantity}."; + $current_stock = $current_stock_row ? (int)$current_stock_row['cantidad'] : 0; + + if ($current_stock < $quantity) { + $response['message'] = "Stock insuficiente. Stock actual: {$current_stock} unidades."; $pdo->rollBack(); echo json_encode($response); exit; } - // 2. Calcular la nueva cantidad - $new_quantity = $current_quantity - $quantity_to_remove; + // 2. Actualizar stock_sedes + $sql_update = "UPDATE stock_sedes SET cantidad = cantidad - :quantity WHERE id_producto = :product_id AND id_sede = :sede_id"; + $stmt_update = $pdo->prepare($sql_update); + $stmt_update->bindParam(':quantity', $quantity, PDO::PARAM_INT); + $stmt_update->bindParam(':product_id', $product_id, PDO::PARAM_INT); + $stmt_update->bindParam(':sede_id', $sede_id, PDO::PARAM_INT); + $stmt_update->execute(); - // 3. Actualizar la tabla de stock - $update_stmt = $pdo->prepare( - "UPDATE stock_sedes SET quantity = :new_quantity WHERE sede_id = :sede_id AND product_id = :product_id" - ); - $update_stmt->execute([ - 'new_quantity' => $new_quantity, - 'sede_id' => $sede_id, - 'product_id' => $product_id - ]); - - // 4. (Opcional pero recomendado) Registrar el movimiento - $movement_stmt = $pdo->prepare( - "INSERT INTO stock_movements (product_id, sede_id, quantity, type, movement_date) VALUES (:product_id, :sede_id, :quantity, 'salida', NOW())" - ); - $movement_stmt->execute([ - 'product_id' => $product_id, - 'sede_id' => $sede_id, - 'quantity' => $quantity_to_remove - ]); + // 3. Registrar el movimiento + $sql_movement = "INSERT INTO stock_movements (product_id, sede_id, tipo_movimiento, cantidad, origen) + VALUES (:product_id, :sede_id, 'salida', :quantity, 'manual')"; + $stmt_movement = $pdo->prepare($sql_movement); + $stmt_movement->bindParam(':product_id', $product_id, PDO::PARAM_INT); + $stmt_movement->bindParam(':sede_id', $sede_id, PDO::PARAM_INT); + $stmt_movement->bindParam(':quantity', $quantity, PDO::PARAM_INT); + $stmt_movement->execute(); $pdo->commit(); + $response['success'] = true; - $response['message'] = "Salida manual registrada con éxito. Stock actualizado a {$new_quantity} unidades."; + $response['message'] = "Se han retirado {$quantity} unidades del stock correctamente."; } catch (PDOException $e) { - $pdo->rollBack(); - // En un entorno de producción, no deberías exponer el mensaje de error detallado. - // Considera registrar el error en un archivo de logs. - $response['message'] = 'Error en la base de datos al procesar la solicitud.'; // Mensaje genérico para el usuario + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + error_log("Error en registrar_salida_manual_api.php: " . $e->getMessage()); + $response['message'] = 'Error en la base de datos. No se pudo registrar la salida.'; } + +} else { + $response['message'] = 'Método no permitido.'; } echo json_encode($response); -?> \ No newline at end of file +?> diff --git a/registro_entrada.php b/registro_entrada.php index 18e71d8..a8937a2 100644 --- a/registro_entrada.php +++ b/registro_entrada.php @@ -1,24 +1,30 @@ query("SELECT id, nombre FROM sedes ORDER BY nombre ASC"); $sedes = $sedes_stmt->fetchAll(PDO::FETCH_ASSOC); + + // Obtener productos + $products_stmt = $pdo->query("SELECT id, nombre FROM products ORDER BY nombre ASC"); + $products = $products_stmt->fetchAll(PDO::FETCH_ASSOC); + } catch (PDOException $e) { - $error_page_load = "Error al cargar las sedes: " . $e->getMessage(); + $error_page_load = "Error al cargar datos iniciales: " . $e->getMessage(); } ?>
-
+
@@ -31,39 +37,88 @@ try {
- Registro de Entrada por Unidad +
-
-
- - + + + + +
+ +
+ +
+ + +
+
+
+ +
+ + +
+
+ +
-
-
- -
- - -
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
- - +
@@ -92,18 +147,20 @@ try { - + \ No newline at end of file diff --git a/registro_salida.php b/registro_salida.php index 9b2b2fb..08c49be 100644 --- a/registro_salida.php +++ b/registro_salida.php @@ -1,19 +1,20 @@ query("SELECT id, nombre FROM sedes ORDER BY nombre ASC"); $sedes = $sedes_stmt->fetchAll(PDO::FETCH_ASSOC); - - // Encontrar el ID de "ALMACEN PRINCIPAL" para usarlo en el script + + // Encontrar el ID de "ALMACEN PRINCIPAL" para la lógica móvil foreach ($sedes as $sede) { if (trim(strtolower($sede['nombre'])) === 'almacen principal') { $almacen_principal_id = $sede['id']; @@ -21,23 +22,18 @@ try { } } -} catch (PDOException $e) { - $error_page_load = "Error al cargar datos: " . $e->getMessage(); -} - -// Obtener productos para el dropdown manual -$products = []; -try { + // Obtener productos $products_stmt = $pdo->query("SELECT id, nombre, sku FROM products ORDER BY nombre ASC"); $products = $products_stmt->fetchAll(PDO::FETCH_ASSOC); + } catch (PDOException $e) { - $error_page_load .= " Error al cargar productos: " . $e->getMessage(); + $error_page_load = "Error al cargar datos iniciales: " . $e->getMessage(); } ?>
-
+
@@ -50,75 +46,89 @@ try {
- Escanear Código de Barras +
-
-
- - + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+ + +
+
+
- - -
- - + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
-
-
- -
- - -
-
- +
- -
-
- Salida Manual de Stock -
-
-

Utilice este formulario para ajustar el inventario y poner en cero el stock de productos antiguos o sin SKU.

-
-
- - -
-
- - -
-
- - -
- -
-
-
-
@@ -145,38 +155,35 @@ try { - + \ No newline at end of file