Hola a todos!
Después de la última publicación, donde utilizábamos los flujos de datos para crear consultas complejas, me comentaban que el tema de las licencias premium podían suponer un problema o un bloqueo al uso de estas herramientas. Y hasta cierto punto, puede que sea así, pero no necesariamente. Imaginad que el último post: AUTOMATIZACIÓN COMPLETA CON POWER PLATFORM: DE POWER APPS A SHAREPOINT Y DATAVERSE CON SINCRONIZACIÓN INTELIGENTE lo podemos hacer con Power Automate, Excel Online y OfficeScript, que os parecería?.
Pues bien, esto es posible y lo voy a demostrar.
Tal y como comenzábamos en el post anterior, partíamos de una lista en SharePoint (SP):

Y es desde aquí donde vamos a obtener toda la información para poder obtener nuestros datos. El segundo paso que debemos hacer es crear una tabla en una ubicación de One Drive:

Y en el Excel vamos a crear las siguientes columnas:

Y ahora ya podemos comenzar a programar nuestro flujo en Power Automate.



Para facilitaros la tarea, os paso el esquema JSON utilizado en el Análisis de archivo JSON (segunda imagen del flujo):
{
"type": "array",
"items": {
"type": "object",
"properties": {
"Proyecto": {
"type": "string"
},
"Tareas Completas": {
"type": "number"
},
"Tareas Pendientes": {
"type": "number"
},
"Tareas Totales": {
"type": "number"
},
"Tareas Vencidas": {
"type": "number"
},
"Horas Estimadas": {
"type": "number"
},
"Horas Reales": {
"type": "number"
},
"Eficiencia Promedio": {
"type": "number"
},
"% Completado": {
"type": "number"
},
"% Desviación": {
"type": "number"
}
}
}
}
Y también el Script que es necesario ejecutar para que calcule todos los elementos. Aquí en lugar de lenguaje M, usamos Typescript para realizar los cálculos:
interface Task {
Proyecto?: string;
Estado?: string;
FechaVencimiento?: string;
HorasEstimadas?: number;
HorasReales?: number;
}
// Estrucutra
interface ProjectData {
Proyecto: string;
TareasCompletas: number;
TareasPendientes: number;
TareasTotales: number;
TareasVencidas: number;
HorasEstimadas: number;
HorasReales: number;
Deviations: number[];
Efficiencies: number[];
}
function main(workbook: ExcelScript.Workbook, tasksJSON: string): string {
// Convertir la cadena JSON a un arreglo tipado de tareas.
const tasks: Task[] = JSON.parse(tasksJSON);
const hoy: Date = new Date();
const projects: { [key: string]: ProjectData } = {};
// Procesamos cada tarea
for (let tarea of tasks) {
const proyecto: string = tarea.Proyecto || "Sin Proyecto";
if (!projects[proyecto]) {
projects[proyecto] = {
Proyecto: proyecto,
TareasCompletas: 0,
TareasPendientes: 0,
TareasTotales: 0,
TareasVencidas: 0,
HorasEstimadas: 0,
HorasReales: 0,
Deviations: [],
Efficiencies: []
};
}
let p = projects[proyecto];
p.TareasTotales++;
// Clasificar según el estado de tarea
if (tarea.Estado === "Completada") {
p.TareasCompletas++;
} else {
p.TareasPendientes++;
}
// Verificar si la tarea está vencida (FechaVencimiento anterior a hoy y no completada).
const fechaVencimiento: Date = new Date(tarea.FechaVencimiento || "");
if (fechaVencimiento < hoy && tarea.Estado !== "Completada") {
p.TareasVencidas++;
}
// Acumular horas.
const horasEst = tarea.HorasEstimadas ? Number(tarea.HorasEstimadas) : 0;
const horasReal = tarea.HorasReales ? Number(tarea.HorasReales) : 0;
p.HorasEstimadas += horasEst;
p.HorasReales += horasReal;
// Calculamos desvia: (HorasReales - HorasEstimadas) / HorasEstimadas.
if (horasEst > 0) {
const deviation = (horasReal - horasEst) / horasEst;
// Solo agregar valores válidos (>= 0 y <= 1e9).
if (deviation >= 0 && deviation <= 1e9) {
p.Deviations.push(deviation);
}
}
// Calculamos eficiencia: HorasEstimadas / HorasReales.
if (horasReal > 0) {
const efficiency = horasEst / horasReal;
if (efficiency >= 0 && efficiency <= 1e9) {
p.Efficiencies.push(efficiency);
}
}
}
// Preparamos array final
const results: Array<{
"Proyecto": string;
"Tareas Completas": number;
"Tareas Pendientes": number;
"Tareas Totales": number;
"Tareas Vencidas": number;
"Horas Estimadas": number;
"Horas Reales": number;
"Eficiencia Promedio": number | null;
"% Completado": number | null;
"% Desviación": number | null;
}> = [];
for (let key in projects) {
const p = projects[key];
// Calculo de indicadores (tiendo en cuenta las tareas) valor individual para cada tarea y luego hacemos promedio.
let eficienciaPromedio: number | null = null;
if (p.Efficiencies.length > 0) {
const sumEff = p.Efficiencies.reduce((acc, val) => acc + val, 0);
eficienciaPromedio = Number((sumEff / p.Efficiencies.length).toFixed(2));
}
let porcentajeDesviacion: number | null = null;
if (p.Deviations.length > 0) {
const sumDev = p.Deviations.reduce((acc, val) => acc + val, 0);
const promedioDev = sumDev / p.Deviations.length;
porcentajeDesviacion = Number((promedioDev * 100).toFixed(2));
}
let porcentajeCompletado: number | null = null;
if (p.TareasTotales > 0) {
const perc = (p.TareasCompletas * 100) / p.TareasTotales;
porcentajeCompletado = Number(perc.toFixed(2));
}
results.push({
"Proyecto": p.Proyecto,
"Tareas Completas": p.TareasCompletas,
"Tareas Pendientes": p.TareasPendientes,
"Tareas Totales": p.TareasTotales,
"Tareas Vencidas": p.TareasVencidas,
"Horas Estimadas": p.HorasEstimadas,
"Horas Reales": p.HorasReales,
"Eficiencia Promedio": eficienciaPromedio,
"% Completado": porcentajeCompletado,
"% Desviación": porcentajeDesviacion
});
}
// pasamos datos
return JSON.stringify(results);
}
Y ahora con este flujo ya montado, si lo ejecutamos va a pasar la información directamente a la tabla que hemos creado en OneDrive:

Como podéis ver los datos son los que estamos esperando y son correctos. Además, aunque el flujo para este ejemplo es manual, se puede crear para ser programable, de forma que tendrá total autonomía.
No voy a volver a crear aquí una app para guardar los datos en la lista. Os dejo como referencia la del post anterior y que tenéis el enlace al inicio del post.
Y esto es todo, espero que sea de utilidad!!
