Hola a todos!
La semana pasada publiqué un imagen en linkedin donde mostraba una consulta de Power Query en Dataflows en el entorno de Power Apps.
Pero qué es un Dataflow: Un dataflow en Power Apps es un proceso automatizado que se utiliza para importar y transformar datos desde una o varias fuentes externas (como Excel, SQL Server, SharePoint, etc.) hacia Dataverse, facilitando su reutilización en aplicaciones, flujos y análisis.
Después de leer esta definición ya nos podemos hacer una idea de los potente que resultan los flujos de datos para nuestros proyecto. Y, por lo tanto, vamos a construir un ejemplo para mostrar su utilidad.
Antes de nada, me remito los datos de una lista de SharePoint (SP), con la que vamos a comenzar, en la que tenemos toda esta información por tareas realizas por proyecto:

La idea es que con estos datos vamos a calcular la siguiente información en un flujo de datos:

Para hacerlo vamos a comenzar por crear el flujo de datos pulsando en Nuevo flujo de datos y le llamamos: Integración_Proyectos_Tareas_SP

El siguiente paso es seleccionar la fuente de datos, en nuestro caso será SP, pero como podéis ver tenemos una amplia variedad para elegir:

Una vez que hemos seleccionado la fuente de datos, configuramos la conexión:

Así:

Y el siguiente paso es seleccionar la lista con la que vamos a trabajar, o cualquier otra lista en realidad, ya que vamos a crear una consulta en Power Query:

Pulsamos crear y aquí tenemos varias formas de realizar la consulta, o bien con el editor avanzado o paso a paso usando los comandos de Query. Yo prefiero usar lenguaje M:

En esa consulta avanzada ya vamos a definir el cálculo de nuestros elementos, os dejo la consulta completa para que veáis cómo lo he realizado, pero es solo una sugerencia, podéis hacerlo a vuestra manera:
let
// Conectamos a SharePoint
Origen = SharePoint.Tables("https://sharepoint.com/sites/SIGNUM_GROUP", [Implementation = "2.0", ViewMode = "All"]),
// Cargamos la lista de tareas
ListaTareas = Table.SelectRows(Origen, each [Title] = "DATAFLOWS_TAREAS"),
ValidarTareas = if Table.IsEmpty(ListaTareas) then error "TAREAS no existe o está vacía" else ListaTareas,
// Expandimos columnas necesarias
Tareas = Table.ExpandTableColumn(ValidarTareas, "Items", {
"Título de la Tarea", "Proyecto", "Asignado a", "Estado", "Fecha de Inicio",
"Fecha de Vencimiento", "Horas Estimadas", "Horas Reales"
}),
// Tipamos los campos
Tipadas = Table.TransformColumnTypes(Tareas, {
{"Proyecto", type text}, {"Estado", type text}, {"Asignado a", type text},
{"Fecha de Inicio", type date}, {"Fecha de Vencimiento", type date},
{"Horas Estimadas", type number}, {"Horas Reales", type number}
}),
// Columnas auxiliares necesarios
Hoy = DateTime.Date(DateTime.LocalNow()),
ConVencida = Table.AddColumn(Tipadas, "Vencida", each [Estado] <> "Completada" and [Fecha de Vencimiento] < Hoy),
ConEficiencia = Table.AddColumn(ConVencida, "Eficiencia", each try [Horas Estimadas] / [Horas Reales] otherwise null),
ConDesviacion = Table.AddColumn(ConEficiencia, "Desviacion", each try ([Horas Reales] - [Horas Estimadas]) / [Horas Estimadas] otherwise null),
// Agrupamos por Proyecto
Agrupado = Table.Group(ConDesviacion, {"Proyecto"}, {
{"Tareas Totales", each Table.RowCount(_), Int64.Type},
{"Tareas Completadas", each Table.RowCount(Table.SelectRows(_, each [Estado] = "Completada")), Int64.Type},
{"Tareas Pendientes", each Table.RowCount(Table.SelectRows(_, each [Estado] <> "Completada")), Int64.Type},
{"Tareas Vencidas", each Table.RowCount(Table.SelectRows(_, each [Vencida] = true)), Int64.Type},
// Controlamos información para que el formato sea adecuado en Dataverse
{"Horas Estimadas", each
let
valores = List.Select(List.RemoveNulls([Horas Estimadas]), each _ >= 0 and _ <= 1000000000),
suma = List.Sum(valores)
in if suma <= 1000000000 then Number.Round(suma, 2) else null,
type number},
{"Horas Reales", each
let
valores = List.Select(List.RemoveNulls([Horas Reales]), each _ >= 0 and _ <= 1000000000),
suma = List.Sum(valores)
in if suma <= 1000000000 then Number.Round(suma, 2) else null,
type number},
{"% Desviación", each
let
valores = List.Select(List.RemoveNulls([Desviacion]), each _ >= 0 and _ <= 1000000000),
promedio = try List.Average(valores) otherwise null
in if promedio <> null then Number.Round(promedio * 100, 2) else null,
type number},
{"% Completado", each
let
total = Table.RowCount(_),
completadas = Table.RowCount(Table.SelectRows(_, each [Estado] = "Completada")),
porcentaje = if total > 0 then (completadas * 100.0) / total else null
in if porcentaje >= 0 and porcentaje <= 100 then Number.Round(porcentaje, 2) else null,
type number},
{"Eficiencia Promedio", each
let
valores = List.Select(List.RemoveNulls([Eficiencia]), each _ >= 0 and _ <= 1000000000),
promedio = try List.Average(valores) otherwise null
in if promedio <> null then Number.Round(promedio, 2) else null,
type number}
})
in
Agrupado
El resultado es que tenemos la consulta con toda la información que necesitamos.

El siguiente paso es publicar la consulta en Dataverse:
Y aquí hay varias opciones, o bien publicamos sobre una tabla que ya existe, No cargamos nada o lo hacemos sobre una tabla nueva. EN este ejemplo lo he realizado sobre una tabla nueva. Con solo pulsar en publicar tenemos todos los datos en una tabla de Dataverse.

Es importante tener en cuenta que, dependiendo del tipo de datos de salida en Power Query, puede que estos no se publiquen correctamente en Dataverse.
En especial, si trabajáis con valores decimales, debéis aseguraros de que el campo en la tabla de Dataverse esté definido como Decimal, ya que Power Query no asigna este tipo automáticamente.
Por ello, mi recomendación es:
- Crear previamente la tabla en Dataverse, definiendo vosotros mismos los tipos de datos adecuados (por ejemplo, Decimal).
- Luego, al configurar el dataflow, seleccionad la opción «Cargar en una tabla existente» para asegurar que los datos se carguen correctamente.
Además de lo anterior, os recuerdo varios puntos a tener en cuenta cuando pasamos información a Dataverse:
Dataverse tiene restricciones sobre cómo deben llamarse las columnas.
- Nombres con paréntesis:
Ejemplo:Duración (días)→ rechazado. - Nombres con símbolos como
%:
Ejemplo:% Avance→ rechazado. - Nombres que comienzan por un número:
Ejemplo:1er Proyecto→ rechazado. - Nombres con caracteres especiales (tildes, ñ, comas, etc.):
Ejemplo:Ámbito,Nombre del Proyecto→ pueden fallar según configuración regional.
Valores nulos en campos clave
- Si el campo
Proyecto(u otro campo clave) tiene valores vacíos o nulos, Dataverse puede rechazar la fila. - Esto es especialmente crítico si ese campo se usa para agrupar, relacionar o si está marcado como obligatorio en Dataverse.
Una vez que ya hemos generado la tabla:

Ya podemos mostrar la información, por ejemplo, en Power Apps:
Y, como que querido hacer el ejemplo completo, he creado un proyecto en Power Apps que va a hacer lo siguiente.
Conectamos la tabla de Dataverse al proyecto y también la lista de SP. En la imagen podéis ver la conexión con la tabla que hemos generado en el DataFlow: cr8d5_DATAFLOWS_TAREAS_1s y el nombre de la lista de SP: DATAFLOWS_TAREAS.

Mostramos la tabla de Dataverse mediante un control de tabla dentro de Power Apps. Para permitir la actualización de los datos, añadimos los campos necesarios utilizando controles como listas desplegables, selectores de fecha y cuadros de texto. El objetivo es que, al completar toda la información y pulsar el botón, los datos se guarden en la lista de SharePoint. A partir de ahí, el flujo de datos actualizará el modelo y, finalmente, se reflejarán los cambios en la tabla de Dataverse que se visualiza dentro de la aplicación.

Pulsamos el botópn de grabar, que por cierto este sería el código:
Patch(
DATAFLOWS_TAREAS;
{
'Título de la Tarea': txtTitulo.Text;
Proyecto: ddProyecto.Selected.Value;
'Asignado a': ddEmpleado.Selected.Value;
Estado: ddEstado.Selected.Value;
'Fecha de Inicio': dpInicio.SelectedDate;
'Fecha de Vencimiento': dpFin.SelectedDate;
'Horas Estimadas': Value(txtEstimadas.Text);
'Horas Reales': Value(txtReales.Text)
}
);;
Reset(txtTitulo);;
Reset(ddProyecto);;
Reset(ddEstado);;
Reset(txtEstimadas);;
Reset(txtReales);;
Reset(ddEmpleado);;
Reset(dpInicio);;
Reset(dpFin);;
Y vemos como se ha grabado en SP:

Y después de actualizar los datos (esta vez manualmente):

y ya tendríamos la información en nuestro proyecto.
Si quisiéramos programar la actualización de la tabla de Dataverse, lo podemos hacer por aquí, pulsando en Editar configuración de actualización.

Por ejemplo yo he seleccionado actualizaciones cada 30 minutos, que es el límite de actualizaciones por día (48).

Y esto es todo, he intentado sintetizar y que las imágenes sean lo más explicativas posibles. Lo he escrito sin ayuda de IA’s y me ha llevado unos días, sobre todo el poder construir los ejemplos y buscar el tiempo para poder construir la publicación. Pero me ha encantado!!
Espero que sea de utilidad!!
