db = Database::getConnection(); } // LISTAR PRODUCTOS public function listarProductos(): array { $sql = "SELECT p.id, p.marca, p.modelo, p.precio, p.stock, (COALESCE(p.descuento, 0) + COALESCE(c.descuento, 0)) AS descuento_total, c.nombre AS categoria, t.nombre AS tipo FROM productos AS p INNER JOIN categorias AS c ON (p.id_categoria = c.id) INNER JOIN tipos AS t ON (p.id_tipo = t.id) WHERE (p.estado IS TRUE) ORDER BY p.id ASC"; $stmt = $this->db->prepare($sql); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_ASSOC); } // LISTAR PRODUCTOS INACTIVOS public function listarProductosInactivos(): array { $sql = "SELECT p.id, p.marca, p.modelo, p.precio, c.nombre AS categoria, t.nombre AS tipo FROM productos AS p INNER JOIN categorias AS c ON (p.id_categoria = c.id) INNER JOIN tipos AS t ON (p.id_tipo = t.id) WHERE (p.estado IS FALSE) ORDER BY p.id ASC"; $stmt = $this->db->prepare($sql); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_ASSOC); } // LISTAR PRODUCTOS PARA VENTA public function listarProductosVenta(): array { $sql = "SELECT p.id, p.marca, p.modelo, p.precio, CAST(p.precio - ((COALESCE(p.descuento, 0) + COALESCE(c.descuento, 0)) * p.precio / 100) AS DECIMAL(10,2)) AS precio_final, p.stock, (COALESCE(p.descuento, 0) + COALESCE(c.descuento, 0)) AS descuento_total FROM productos AS p INNER JOIN categorias AS c ON (p.id_categoria = c.id) INNER JOIN tipos AS t ON (p.id_tipo = t.id) WHERE (p.estado IS TRUE) AND (p.id_tipo = 1) AND (p.precio IS NOT NULL) AND (p.stock <> 0) ORDER BY p.id ASC"; $stmt = $this->db->prepare($sql); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_ASSOC); } // LISTAR PORDUCTOS PARA PEDIDO public function listarProductosPedido(): array { $sql = "SELECT p.id, p.marca, p.modelo, p.stock FROM productos AS p INNER JOIN tipos AS t ON (p.id_tipo = t.id) WHERE (p.estado IS TRUE) ORDER BY p.id ASC"; $stmt = $this->db->prepare($sql); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_ASSOC); } // LISTAR INSUMOS PARA SERVICIO public function listarInsumosServicio(): array { $sql = "SELECT p.id, p.marca, p.modelo, p.precio, p.stock FROM productos AS p INNER JOIN categorias AS c ON (p.id_categoria = c.id) INNER JOIN tipos AS t ON (p.id_tipo = t.id) WHERE (p.estado IS TRUE) AND (p.id_tipo = 2) AND (p.precio IS NOT NULL) AND (p.stock <> 0) ORDER BY p.id ASC"; $stmt = $this->db->prepare($sql); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_ASSOC); } // CREAR PRODUCTO public function crearProducto(array $data): bool { $sql = "INSERT INTO productos (marca, modelo, precio, stock, descuento, estado, id_categoria, id_tipo) VALUES (:marca, :modelo, :precio, :stock, :descuento, :estado, :id_categoria, :id_tipo)"; $stmt = $this->db->prepare($sql); return $stmt->execute([ ':marca' => $data['marca'], ':modelo' => $data['modelo'], ':precio' => $data['precio'], ':stock' => $data['stock'], ':descuento' => $data['descuento'], ':estado' => 1, // Cuando un producto se crea, automáticamente se setea el estado en true, es decir está activo ':id_categoria' => $data['id_categoria'], ':id_tipo' => $data['id_tipo'] ]); } // OBTENER PRODUCTO MEDIANTE ID public function obtenerPorId(int $id): ?array { $sql = "SELECT p.id, p.marca, p.modelo, p.precio, p.stock, p.descuento, p.id_categoria, p.id_tipo, pd.id_pedido AS id_pedido FROM productos AS p LEFT JOIN pedido_detalle AS pd ON (p.id = pd.id_producto) WHERE p.id = :id ORDER BY pd.id_pedido DESC LIMIT 1"; $stmt = $this->db->prepare($sql); $stmt->execute([':id' => $id]); $producto = $stmt->fetch(PDO::FETCH_ASSOC); return $producto ?: null; } // OBTENER PRODUCTO POR ID PARA CAJA public function obtenerPorIdParaCaja(int $id): ?array { $sql = "SELECT p.id, p.marca, p.modelo, p.precio, COALESCE(p.descuento, 0) AS descuento_producto, COALESCE(c.descuento, 0) AS descuento_categoria, p.stock FROM productos AS p LEFT JOIN categorias AS c ON (c.id = p.id_categoria) WHERE (p.id = :id) AND (p.estado IS TRUE) AND (p.id_tipo = 1) LIMIT 1"; $stmt = $this->db->prepare($sql); $stmt->execute([':id' => $id]); return $stmt->fetch(PDO::FETCH_ASSOC) ?: null; } // OBTENER PRODUCTOS DE PEDIDO POR ID PARA CAJA public function obtenerPorIdParaCajaPedido(int $id): ?array { $sql = "SELECT p.id, p.marca, p.modelo, p.stock, COALESCE(c.porcentaje_ganancia, 0) AS porcentaje_ganancia FROM productos AS p LEFT JOIN categorias AS c ON (c.id = p.id_categoria) WHERE (p.id = :id) AND (p.estado IS TRUE) LIMIT 1"; $stmt = $this->db->prepare($sql); $stmt->execute([':id' => $id]); return $stmt->fetch(PDO::FETCH_ASSOC) ?: null; } // OBTENER INSUMO DE SERVICIO POR ID PARA CAJA public function obtenerPorIdParaCajaServicio(int $id): ?array { $sql = "SELECT p.id, p.marca, p.modelo, p.precio, p.stock FROM productos AS p LEFT JOIN categorias AS c ON (c.id = p.id_categoria) WHERE (p.id = :id) AND (p.estado IS TRUE) AND (p.id_tipo = 2) LIMIT 1"; $stmt = $this->db->prepare($sql); $stmt->execute([':id' => $id]); return $stmt->fetch(PDO::FETCH_ASSOC) ?: null; } // EDITAR PRODUCTO public function actualizarProducto(int $id, array $data): bool { if (empty($data)) { return false; } $campos = []; $params = []; foreach ($data as $campo => $valor) { $campos[] = "$campo = :$campo"; $params[":$campo"] = $valor; } $params[':id'] = $id; $sql = "UPDATE productos SET " . implode(', ', $campos) . " WHERE id = :id"; $stmt = $this->db->prepare($sql); return $stmt->execute($params); } // ACTUALIZAR ESTADO (ACTIVO / INACTIVO) public function actualizarEstado(int $idProducto, bool $nuevoEstado): bool { try { $this->db->beginTransaction(); // Actualizamaos datos del producto $sql1 = "UPDATE productos SET estado = :estado, precio = NULL, stock = NULL, descuento = NULL WHERE id = :id"; $stmt1 = $this->db->prepare($sql1); $stmt1->bindValue(':estado', $nuevoEstado, PDO::PARAM_BOOL); $stmt1->bindValue(':id', $idProducto, PDO::PARAM_INT); $stmt1->execute(); // Actualizamos los stock y cantidades de pedido_detalle $sql2 = "UPDATE pedido_detalle SET cantidad_vendida = cantidad WHERE id_producto = :id"; $stmt2 = $this->db->prepare($sql2); $stmt2->bindValue(':id', $idProducto, PDO::PARAM_INT); $stmt2->execute(); $this->db->commit(); return true; } catch (Exception $e) { $this->db->rollBack(); // Log interno error_log($e->getMessage()); return false; } } // LISTAR CATEGORIAS public function listarCategorias(): array { $sql = "SELECT id, nombre, porcentaje_ganancia, descuento FROM categorias ORDER BY nombre ASC"; $stmt = $this->db->prepare($sql); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_ASSOC); } // VERIFICAR SI EXISTE UNA CATEGORIA public function existeCategoria(int $id): bool { $sql = "SELECT 1 FROM categorias WHERE id = :id LIMIT 1"; $stmt = $this->db->prepare($sql); $stmt->execute([':id' => $id]); return (bool) $stmt->fetchColumn(); } // OBTENER CATEGORIA POR ID public function obtenerPorIdCategoria(int $id): ?array { $sql = "SELECT id, nombre, porcentaje_ganancia, descuento FROM categorias WHERE id = :id"; $stmt = $this->db->prepare($sql); $stmt->execute([':id' => $id]); $categoria = $stmt->fetch(PDO::FETCH_ASSOC); return $categoria ?: null; } // CREAR CATEGORIA public function crearCategoria(array $data): bool { $sql = "INSERT INTO categorias (nombre, porcentaje_ganancia, descuento) VALUES (:nombre, :porcentaje_ganancia, :descuento)"; $stmt = $this->db->prepare($sql); return $stmt->execute([ ':nombre' => $data['nombre'], ':porcentaje_ganancia' => $data['porcentaje_ganancia'], ':descuento' => $data['descuento'] ]); } // EDITAR CATEGORIA public function actualizarCategoria(int $id, array $data): bool { if (empty($data)) { return false; } $campos = []; $params = []; foreach ($data as $campo => $valor) { $campos[] = "$campo = :$campo"; $params[":$campo"] = $valor; } $params[':id'] = $id; $sql = "UPDATE categorias SET " . implode(', ', $campos) . " WHERE id = :id"; $stmt = $this->db->prepare($sql); return $stmt->execute($params); } // ACTUALIZAR PRECIOS DE PRODUCTOS AL EDITAR LA CATEGORIA public function actualizarPreciosPorCategoria(int $idCategoria, array $data, array $costosProductos): bool { try { $this->db->beginTransaction(); // Actualizamos la categoría $ok = $this->actualizarCategoria($idCategoria, $data); if (!$ok) { $this->db->rollBack(); return false; } // Obtenemos el nuevo porcentaje de ganancia $stmt = $this->db->prepare(" SELECT porcentaje_ganancia FROM categorias WHERE id = :id "); $stmt->execute([':id' => $idCategoria]); $categoria = $stmt->fetch(PDO::FETCH_ASSOC); if (!$categoria) { $this->db->rollBack(); return false; } $porcentaje_ganancia = (float) $categoria['porcentaje_ganancia']; $stmt = $this->db->prepare(" UPDATE productos SET precio = :precio WHERE (id = :id) AND (id_categoria = :idCategoria) "); foreach ($costosProductos as $c) { // Validaciones defensivas if (empty($c['id_producto']) || $c['costo'] === null) { continue; } $costo = (float) $c['costo']; // Fórmula correcta $nuevoPrecio = (100 + $porcentaje_ganancia) * $costo / 100; $stmt->execute([ ':precio' => $nuevoPrecio, ':id' => $c['id_producto'], ':idCategoria' => $idCategoria ]); } $this->db->commit(); return true; } catch (Exception $e) { $this->db->rollBack(); // Log interno error_log($e->getMessage()); return false; } } // LISTAR TIPOS DE PRODUCTOS public function listarTipos(): array { $sql = "SELECT id, nombre FROM tipos ORDER BY id ASC"; $stmt = $this->db->prepare($sql); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_ASSOC); } // VERIFICAR SI EXISTE EL TIPO DE PRODUCTO public function existeTipo(int $id): bool { $sql = "SELECT 1 FROM tipos WHERE id = :id LIMIT 1"; $stmt = $this->db->prepare($sql); $stmt->execute([':id' => $id]); return (bool) $stmt->fetchColumn(); } // OBTENER TIPO POR ID public function obtenerPorIdTipo(int $tipo): ?array { $sql = "SELECT id, nombre FROM tipos WHERE id = :id"; $stmt = $this->db->prepare($sql); $tipo = $stmt->execute(); return $tipo ?: null; } // OBTENER DESCUENTOS DE PRODUCTOS EN BASE A UNA CATEGORIA public function obtenerDescuento(int $idCategoria): ?array { $sql = "SELECT productos.id, productos.descuento FROM productos INNER JOIN categorias ON (categorias.id = productos.id_categoria) WHERE (categorias.id = :idCategoria)"; $stmt = $this->db->prepare($sql); $stmt->execute([':idCategoria' => $idCategoria]); $resultado = $stmt->fetchAll(PDO::FETCH_ASSOC); return !empty($resultado) ? $resultado : null; } // BÚSQUEDA EN INPUT DE DESCRIPCIÓN DEL EQUIPO (MÓDULO SERVICIOS) public function busquedaParaServicio(): array { $sql = "SELECT marca, modelo FROM productos WHERE (id_tipo = 1)"; $stmt = $this->db->prepare($sql); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_ASSOC); } }