Friday, November 25, 2011

Aprobar una Tarea Programáticamente

Hola de nuevo. Este es un tema que varios han comentado bastante en Foros, Blogs, y bueno cada medio que permita dar a entender la problemática.
En resumen lo que necesitábamos en nuestro caso era poder Aprobar una Tarea asociada a un WorkFlow diseñado con SharePoint Designer, pero que al tiempo el WorkFlow vaya avanzando.
Uno de los inconvenientes que los usuarios finales encuentran con los WorkFlows de SharePoint Designer es tener que completar las tareas asignadas, pero de paso también tener que aprobar el ítem de la lista que tiene asociado el WorkFlow.
Para evitar esto, podemos crear una WebPart que nos permita tanto aprobar (realizar la tarea y cambiar el estado del ítem), como puede ser rechazar o finalmente cancelar en caso de no querer ejecutar ninguna acción en el momento.
Supongamos entonces que tenemos un WorkFlow bastante simple, con 3 pasos, durante los cuales un primer paso es asignar una tarea a un Primer Aprobador. Luego en el siguiente paso el primer aprobador requiere Revisar y Aprobar un ítem de una lista o quizá un documento en una biblioteca, y quiere poder hacer todo desde un único punto, y no tener que vérselas con la opción de Realizar Tarea. En caso que el primer aprobador apruebe, entonces viene un tercer paso donde el segundo aprobador da por terminado el proceso Aprobando o Rechazando el ítem.
A continuación se puede apreciar el WorkFlow diseñado:
Task1
Cuando se crea un WorkFlow en SharePoint Designer, este a su vez crea una serie de formularios ASPX personalizados que se pueden encontrar en la siguiente localización, junto a los archivos de configuración y reglas del WorkFlow. Esas páginas ASPX se corresponden o sirven con mecanismo que permite la Realización de la Tarea, que se ha configurado como una Acción en algún paso del WorkFlow, por ejemplo en los pasos Revisar y Aprobar 1 del WorkFlow de este ejemplo.
Task2
En rojo se enmarcan las páginas ASPX respectivas de cada Acción de Tarea en los pasos del WorkFlow.
Pero llega el momento de crear la WebPart que permita entonces reemplazar la WebPart por defecto de cada página ASPX asociada a una Tarea. La WebPart por defecto lo único que permite realizar es la acción de Completar la Tarea, que es lo que queremos evitar, y que mejor eso se haga y también se modifique automáticamente el estado del WorkFlow.
La lista que se asociará con el WorkFlow deberá contener un campo llamado Estado de tipo elección y campo llamado Aprobador de tipo Texto. Los dos campos podrán estar ocultos en la lista porque son únicamente un medio de ejecución y control para la WebPart personalizada.
No voy a explicar cómo construir la WebPart ya se aen WSPBuilder para SharePoint 2007 o una WebPart visual en SharePoint 2010. Dejo el código a continuación. Este ejemplo asume que la WebPart utiliza un control ASCX. En el cual estarán las tres acciones de Aprobar, Rechazar y Cancelar, que son básicamente botones.
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Workflow;
using Microsoft.SharePoint.Utilities;
using System.Collections;
using System.Globalization;
namespace WSPBuilderCOA
{
    public partial class EditTask : System.Web.UI.UserControl
    {
        private string url;
        public string Url
        {
            set
            {
                url = value;
            }
            get
            {
                return url;
            }
        }
        protected void Page_Load(object sender, EventArgs e)
        {
            //this.lblMensaje = "";
        }
        protected void btnAprobar_Click(object sender, EventArgs e)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite site = new SPSite(url))
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                        web.AllowUnsafeUpdates = true;
                        try
                        {
                            SPList task = web.Lists["Tareas"];
                            SPListItem item = task.Items.GetItemById(Convert.ToInt32(Request.Params["ID"]));
                            if (item["WorkflowListId"] != null)
                            {
                                Guid sourceListID = new Guid(item["WorkflowListId"].ToString());
                                SPList sourceList = web.Lists.GetList(sourceListID, true);
                                int sourceListItemID = Convert.ToInt32(item["WorkflowItemId"]);
                                SPListItem sourceListItem = sourceList.GetItemById(sourceListItemID);
                                //El primer aprobador realiza la tarea y cambia el estado del ítem a Aprobado en Primera Instancia
                                if (sourceListItem["Aprobador"].ToString() == "Aprobador 1" && Convert.ToString(sourceListItem["Estado"]) == "En Revisión")
                                {
                                    sourceListItem["Estado"] = "Aprobado en Primera Instancia";
                                    sourceListItem["Aprobador"] = "Aprobador 1";
                                    sourceListItem.Update();
                                    //Get the workflow instance id from Task item
                                    Guid taskWorkflowInstanceID = new Guid(item["WorkflowInstanceID"].ToString());
                                    SPWorkflow workflow = item.Workflows[taskWorkflowInstanceID];
                                    SPWorkflowTask wfTask = workflow.Tasks[item.UniqueId];
                                    Hashtable ht = new Hashtable();
                                    ht[SPBuiltInFieldId.Completed] = "TRUE";
                                    ht["Completed"] = "TRUE";
                                    ht[SPBuiltInFieldId.PercentComplete] = 1.0f;
                                    ht["PercentComplete"] = 1.0f;
                                    ht["Status"] = "Completed";
                                    ht[SPBuiltInFieldId.TaskStatus] = SPResource.GetString(new CultureInfo((int)wfTask.Web.Language, false), Strings.WorkflowStatusInProgress, new object[0]);
                                    ht[SPBuiltInFieldId.WorkflowOutcome] = "Approved";
                                    ht["TaskStatus"] = "Approved";
                                    ht["FormData"] = SPWorkflowStatus.InProgress;

                                    SPWorkflowTask.AlterTask((wfTask as SPListItem), ht, true);
                                    Response.Redirect(Request.Params["Source"]);
                                }
                                else
                                {
                                    if (sourceListItem["Aprobador"].ToString() == "Aprobador 1" && Convert.ToString(sourceListItem["Estado"]) == "Aprobado en Primera Instancia")
                                    {
                                        sourceListItem["Estado"] = "Aprobado en Segunda Instancia";
                                        sourceListItem["Aprobador"] = "Aprobador 2";
                                        sourceListItem.Update();
                                        //Get the workflow instance id from Task item
                                        Guid taskWorkflowInstanceID = new Guid(item["WorkflowInstanceID"].ToString());
                                        SPWorkflow workflow = item.Workflows[taskWorkflowInstanceID];
                                        SPWorkflowTask wfTask = workflow.Tasks[item.UniqueId];
                                        Hashtable ht = new Hashtable();
                                        ht[SPBuiltInFieldId.Completed] = "TRUE";
                                        ht["Completed"] = "TRUE";
                                        ht[SPBuiltInFieldId.PercentComplete] = 1.0f;
                                        ht["PercentComplete"] = 1.0f;
                                        ht["Status"] = "Completed";
                                        ht[SPBuiltInFieldId.TaskStatus] = SPResource.GetString(new CultureInfo((int)wfTask.Web.Language, false), Strings.WorkflowStatusInProgress, new object[0]);
                                        ht[SPBuiltInFieldId.WorkflowOutcome] = "Approved";
                                        ht["TaskStatus"] = "Approved";
                                        ht["FormData"] = SPWorkflowStatus.InProgress;
                                        SPWorkflowTask.AlterTask((wfTask as SPListItem), ht, true);
                                        Response.Redirect(Request.Params["Source"]);
                                    }
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            //lblMensaje.Text = "Ha ocurrido un error aprobando: " + ex.Message + " - " + ex.Source;
                        }
                        finally
                        {
                            web.AllowUnsafeUpdates = false;
                        }
                    }
                }
            });
        }

        protected void btnRechazar_Click(object sender, EventArgs e)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite site = new SPSite(url))
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                        web.AllowUnsafeUpdates = true;
                        try
                        {
                            SPList task = web.Lists["Tareas"];
                            SPListItem item = task.Items.GetItemById(Convert.ToInt32(Request.Params["ID"]));
                            if (item["WorkflowListId"] != null)
                            {
                                Guid sourceListID = new Guid(item["WorkflowListId"].ToString());
                                SPList sourceList = web.Lists.GetList(sourceListID, true);
                                int sourceListItemID = Convert.ToInt32(item["WorkflowItemId"]);
                                SPListItem sourceListItem = sourceList.GetItemById(sourceListItemID);
                                //El primer aprobador realiza la tarea y cambia el estado del ítem a Aprobado en Primera Instancia
                                if (sourceListItem["Aprobador"].ToString() == "Aprobador 1" && Convert.ToString(sourceListItem["Estado"]) == "En Revisión")
                                {
                                    sourceListItem["Estado"] = "Rechazado";
                                    sourceListItem["Aprobador"] = "Aprobador 1";
                                    sourceListItem.Update();
                                    //Get the workflow instance id from Task item
                                    Guid taskWorkflowInstanceID = new Guid(item["WorkflowInstanceID"].ToString());
                                    SPWorkflow workflow = item.Workflows[taskWorkflowInstanceID];
                                    SPWorkflowTask wfTask = workflow.Tasks[item.UniqueId];
                                    Hashtable ht = new Hashtable();
                                    ht[SPBuiltInFieldId.Completed] = "TRUE";
                                    ht["Completed"] = "TRUE";
                                    ht[SPBuiltInFieldId.PercentComplete] = 1.0f;
                                    ht["PercentComplete"] = 1.0f;
                                    ht["Status"] = "Completed";
                                    ht[SPBuiltInFieldId.TaskStatus] = SPResource.GetString(new CultureInfo((int)wfTask.Web.Language, false), Strings.WorkflowStatusInProgress, new object[0]);
                                    ht[SPBuiltInFieldId.WorkflowOutcome] = "Approved";
                                    ht["TaskStatus"] = "Approved";
                                    ht["FormData"] = SPWorkflowStatus.InProgress;
                                    SPWorkflowTask.AlterTask((wfTask as SPListItem), ht, true);
                                    Response.Redirect(Request.Params["Source"]);
                                   
                                }
                                else
                                {
                                    if (sourceListItem["Aprobador"].ToString() == "Aprobador 1" && Convert.ToString(sourceListItem["Estado"]) == "Aprobado en Primera Instancia")
                                    {
                                        sourceListItem["Estado"] = "Rechazado";
                                        sourceListItem["Aprobador"] = "Aprobador 2";
                                        sourceListItem.Update();
                                        //Get the workflow instance id from Task item
                                        Guid taskWorkflowInstanceID = new Guid(item["WorkflowInstanceID"].ToString());
                                        SPWorkflow workflow = item.Workflows[taskWorkflowInstanceID];
                                        SPWorkflowTask wfTask = workflow.Tasks[item.UniqueId];
                                        Hashtable ht = new Hashtable();
                                        ht[SPBuiltInFieldId.Completed] = "TRUE";
                                        ht["Completed"] = "TRUE";
                                        ht[SPBuiltInFieldId.PercentComplete] = 1.0f;
                                        ht["PercentComplete"] = 1.0f;
                                        ht["Status"] = "Completed";
                                        ht[SPBuiltInFieldId.TaskStatus] = SPResource.GetString(new CultureInfo((int)wfTask.Web.Language, false), Strings.WorkflowStatusInProgress, new object[0]);
                                        ht[SPBuiltInFieldId.WorkflowOutcome] = "Approved";
                                        ht["TaskStatus"] = "Approved";
                                        ht["FormData"] = SPWorkflowStatus.InProgress;
                                        SPWorkflowTask.AlterTask((wfTask as SPListItem), ht, true);
                                        Response.Redirect(Request.Params["Source"]);
                                    }
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            //lblMensaje.Text = "Ha ocurrido un error aprobando: " + ex.Message + " - " + ex.Source;
                        }
                        finally
                        {
                            web.AllowUnsafeUpdates = false;
                        }
                    }
                }
            });
        }
    }
}
El código es únicamente como demostración, no hemos revisado ninguna buena práctica o que quede mejor ordenado y estructurado porque no es el objetivo de esta entrada. Lo realmente interesante de todo eso, es la parte donde se ejecutan las acciones que permiten Aprobar y Terminar cada tarea asociada, pero que adicionalmente cambian el campo Estado de la lista y que hace que el WorkFlow pueda ir avanzando acorde a la lógica diseñada en SharePoint Designer.
En la siguiente imagen se aprecian la WebPart personalizada y la que ofrece SharePoint por defecto para cada Tarea. El ejemplo muestra lo que vería el usuario Aprobador 2 cuando va a Editar su Tarea asignada.
Task3
Feliz Tarea Programática.

No comments: