none
Crear y publicar un proyecto de microsoft office Project

    Pregunta

  • Hola, quiero desde una página aspx muy básica, varios textboxt y un botton, crear en un dominio un proyecto (el acceso lo tengo con autenticación windows) con el nombre de un textbox.
    Es decir, el textbox.text es proyecto1.

    Entonces en nuestra red interna se creará el proyecto "Proyecto1".

    Alguien me puede pasar algo de información sobre como hacerlo? estoy buscando pero no encuentro nada claro ni ningun enlace que me explique desde 0, ya que soy nuevo..., gracias por su ayuda.
    piskui
    lunes, 04 de enero de 2010 16:33

Respuestas

  • Hola piskui.

    Has topado con un buen tema jejeje, si que es verdad que no existe mucha informacion por la red, yo llevo programando para project mucho tiempo y cada vez que busco algo nuevo cuesta bastante.

    Supongo que hablamos de project 2007 en adelante (ya que versiones anteriores no disponen del PSI y se tiene que programar mediante xml y es otra historia)

    bueno, tratar de explicar desde 0 seria muy largo, entonces lo que voy a hacer es ponerte un ejemplo para crearte un proyecto. (mas vale un codigo que mil palabras).
    Ademas, deberias si no lo has echo ya, descargarte la SDK de project en:
    http://www.microsoft.com/downloads/details.aspx?FamilyId=2672F6F9-7028-4B30-99A2-18CB1EED1ABE&displaylang=en

    En la SDK esta la documentacion (con bastantes ejemplos pero documentada muy pobremente) y tambien tienes codigos de ejemplo que te ayudaran para la mayoria de funcionalidades basicas (como la creacion de proyectos y tareas).

    Una vez dicho esto, aqui te dejo un codigo basico para crear un proyecto (Sin hacer uso de ninguna plantilla):


    /// <summary>
    /// Crear un proyecto en project server
    /// </summary>
    /// <param name="AutoPublish">si es 'true' se autopublica el proyecto</param>
    /// <param name="ProjectName">Nombre del proyecto</param>
    /// <param name="StartDate">Fecha de inicio del proyecto</param>
    /// <param name="AutorProyecto">Autor del proyecto</param>
    public void CrearProyecto(bool AutoPublish
        , string ProjectName
        , string StartDate
        , string AutorProyecto
        )
    {
        // Crear el DataSet para projectos de projectServer
        ProjectWebSvc.ProjectDataSet dsProject = new ProjectWebSvc.ProjectDataSet();
        // Crear un objeto fila de projecto para añadir un projecto nuevo
        ProjectWebSvc.ProjectDataSet.ProjectRow projectRow = dsProject.Project.NewProjectRow();
    
        // crear un GUID nuevo para el proyecto
        Guid projectGuid = Guid.NewGuid();
    
        // establecer los parametros del nuevo proyecto
        projectRow.PROJ_UID = projectGuid;
        projectRow.PROJ_NAME = ProjectName;
        projectRow.PROJ_INFO_START_DATE = DateTime.Parse(StartDate);
        projectRow.PROJ_TYPE = Convert.ToInt32(PSLibrary.Project.ProjectType.Project);
        projectRow.PROJ_PROP_AUTHOR = m_version + "\\" + AutorProyecto;
        //projectRow.PROJ_PROP_COMPANY = "Mi compañia";
    
        // Añadir la nueva fila con el proyecto
        dsProject.Project.AddProjectRow(projectRow);
    
        //--------------------------------
        // crear proyecto
    
        // guid para la cola de trabajo
        Guid jobGuid = Guid.NewGuid();
        bool validateOnly = false;
        // meter en la cola de project la creacion del proyecto
        project.QueueCreateProject(jobGuid, dsProject, validateOnly);
        // esperar a que se procese el trabajo de la creacion del proyecto
        WaitForQueue(jobGuid);
    
        // crear un nuevo guid para publicar el proyecto
        jobGuid = Guid.NewGuid();
        string wssUrl = "";
        // meter en la cola de proyect la tarea de publicar el proyecto
        project.QueuePublish(jobGuid, projectGuid, AutoPublish, wssUrl);
        // esperar a que termine de procesar la publicacion
        WaitForQueue(jobGuid);
    }
    


    Este es el codigo para crear un proyecto basico, primero se obtiene el dataset con los proyectos de project, se añade la fila con el proyecto y se envia a la cola de project para crearlo.

    la variable ProjectWebSvc es el servicio web que apunta al servidor de project en la ruta relativa:  /_vti_bin/PSI/project.asmx

    en cuanto a la funcion 'WaitFoeQueue()' es una funcion de la SDK de project, pero a lo largo del tiempo que llevo programando he visto que en la documentacion hay varias y otras que he encontrado en internet, te pongo una personal que he creado cogiendo lo que mas me ha gustado de todas:

    /// <summary>
    /// Esperar a que se procese la cola de jobs para la insercion en proyectserver mediante jobs
    /// </summary>        
    /// <param name="jobGuid">GUID del job a esperar que se procese</param>
    private static void WaitForQueue(Guid jobGuid)
    {
        // Inicializar objetos para procesar la espera           
        QueueSystemWebSvc.QueueStatusDataSet queueStatusDataSet = new QueueSystemWebSvc.QueueStatusDataSet();
        QueueSystemWebSvc.QueueStatusRequestDataSet queueStatusRequestDataSet = new QueueSystemWebSvc.QueueStatusRequestDataSet();
    
        QueueSystemWebSvc.QueueStatusRequestDataSet.StatusRequestRow statusRequestRow = queueStatusRequestDataSet.StatusRequest.NewStatusRequestRow();
        statusRequestRow.JobGUID = jobGuid;
        statusRequestRow.JobGroupGUID = Guid.NewGuid();
        statusRequestRow.MessageType = -1;
        queueStatusRequestDataSet.StatusRequest.AddStatusRequestRow(statusRequestRow);
    
        // Variables locales para usar
        bool inProcess = true;
        DateTime startTime = DateTime.Now;
        int successState = (int)PSLibrary.QueueConstants.JobState.Success;
        int failedState = (int)PSLibrary.QueueConstants.JobState.Failed;
        int blockedState = (int)PSLibrary.QueueConstants.JobState.CorrelationBlocked;
    
        // continuar mientras se esta procesando
        while (inProcess)
        {
            try
            {
                // Leer y obtener el estatus del job en projectServer
                queueStatusDataSet = m_queueSystem.ReadJobStatus(queueStatusRequestDataSet, false, QueueSystemWebSvc.SortColumn.Undefined, QueueSystemWebSvc.SortOrder.Undefined);
            }
            catch (Exception e)
            {
                throw e;
            }
    
            // Recorrer todas las filas obtenidas con los estatus
            foreach (QueueSystemWebSvc.QueueStatusDataSet.StatusRow statusRow in queueStatusDataSet.Status)
            {
                // Si existe algun error se muestra su excepcion
                if ((statusRow["ErrorInfo"] != System.DBNull.Value
                    && checkStatusRowHasError(statusRow["ErrorInfo"].ToString()) == true)
                    || statusRow.JobCompletionState == blockedState
                    || statusRow.JobCompletionState == failedState)
                {
                    inProcess = false;
                    throw (new ApplicationException("Queue request failed", new Exception(statusRow.ErrorInfo)));
                }
    
                // Si el estatus es correcto, se para el proceso de jobs para salir de la funcion
                if (statusRow.JobCompletionState == successState)
                {
                    inProcess = false;
                }
    
                // Hacer una pausa y seguir procesando jobs
                else
                {
                    System.Threading.Thread.Sleep(500);
                }
            }
        }
    }

    No intentes analizar esta ultima funcion ahora al principio, ya que hay que saber como funciona el PSI para entenderla.

    En cualquier caso esto te debe servir para tu proposito, pero si necesitas mas ayuda comentamelo.








    Saludos
    David González
    • Marcado como respuesta piskui martes, 05 de enero de 2010 14:33
    martes, 05 de enero de 2010 8:31

Todas las respuestas

  • Hola piskui.

    Has topado con un buen tema jejeje, si que es verdad que no existe mucha informacion por la red, yo llevo programando para project mucho tiempo y cada vez que busco algo nuevo cuesta bastante.

    Supongo que hablamos de project 2007 en adelante (ya que versiones anteriores no disponen del PSI y se tiene que programar mediante xml y es otra historia)

    bueno, tratar de explicar desde 0 seria muy largo, entonces lo que voy a hacer es ponerte un ejemplo para crearte un proyecto. (mas vale un codigo que mil palabras).
    Ademas, deberias si no lo has echo ya, descargarte la SDK de project en:
    http://www.microsoft.com/downloads/details.aspx?FamilyId=2672F6F9-7028-4B30-99A2-18CB1EED1ABE&displaylang=en

    En la SDK esta la documentacion (con bastantes ejemplos pero documentada muy pobremente) y tambien tienes codigos de ejemplo que te ayudaran para la mayoria de funcionalidades basicas (como la creacion de proyectos y tareas).

    Una vez dicho esto, aqui te dejo un codigo basico para crear un proyecto (Sin hacer uso de ninguna plantilla):


    /// <summary>
    /// Crear un proyecto en project server
    /// </summary>
    /// <param name="AutoPublish">si es 'true' se autopublica el proyecto</param>
    /// <param name="ProjectName">Nombre del proyecto</param>
    /// <param name="StartDate">Fecha de inicio del proyecto</param>
    /// <param name="AutorProyecto">Autor del proyecto</param>
    public void CrearProyecto(bool AutoPublish
        , string ProjectName
        , string StartDate
        , string AutorProyecto
        )
    {
        // Crear el DataSet para projectos de projectServer
        ProjectWebSvc.ProjectDataSet dsProject = new ProjectWebSvc.ProjectDataSet();
        // Crear un objeto fila de projecto para añadir un projecto nuevo
        ProjectWebSvc.ProjectDataSet.ProjectRow projectRow = dsProject.Project.NewProjectRow();
    
        // crear un GUID nuevo para el proyecto
        Guid projectGuid = Guid.NewGuid();
    
        // establecer los parametros del nuevo proyecto
        projectRow.PROJ_UID = projectGuid;
        projectRow.PROJ_NAME = ProjectName;
        projectRow.PROJ_INFO_START_DATE = DateTime.Parse(StartDate);
        projectRow.PROJ_TYPE = Convert.ToInt32(PSLibrary.Project.ProjectType.Project);
        projectRow.PROJ_PROP_AUTHOR = m_version + "\\" + AutorProyecto;
        //projectRow.PROJ_PROP_COMPANY = "Mi compañia";
    
        // Añadir la nueva fila con el proyecto
        dsProject.Project.AddProjectRow(projectRow);
    
        //--------------------------------
        // crear proyecto
    
        // guid para la cola de trabajo
        Guid jobGuid = Guid.NewGuid();
        bool validateOnly = false;
        // meter en la cola de project la creacion del proyecto
        project.QueueCreateProject(jobGuid, dsProject, validateOnly);
        // esperar a que se procese el trabajo de la creacion del proyecto
        WaitForQueue(jobGuid);
    
        // crear un nuevo guid para publicar el proyecto
        jobGuid = Guid.NewGuid();
        string wssUrl = "";
        // meter en la cola de proyect la tarea de publicar el proyecto
        project.QueuePublish(jobGuid, projectGuid, AutoPublish, wssUrl);
        // esperar a que termine de procesar la publicacion
        WaitForQueue(jobGuid);
    }
    


    Este es el codigo para crear un proyecto basico, primero se obtiene el dataset con los proyectos de project, se añade la fila con el proyecto y se envia a la cola de project para crearlo.

    la variable ProjectWebSvc es el servicio web que apunta al servidor de project en la ruta relativa:  /_vti_bin/PSI/project.asmx

    en cuanto a la funcion 'WaitFoeQueue()' es una funcion de la SDK de project, pero a lo largo del tiempo que llevo programando he visto que en la documentacion hay varias y otras que he encontrado en internet, te pongo una personal que he creado cogiendo lo que mas me ha gustado de todas:

    /// <summary>
    /// Esperar a que se procese la cola de jobs para la insercion en proyectserver mediante jobs
    /// </summary>        
    /// <param name="jobGuid">GUID del job a esperar que se procese</param>
    private static void WaitForQueue(Guid jobGuid)
    {
        // Inicializar objetos para procesar la espera           
        QueueSystemWebSvc.QueueStatusDataSet queueStatusDataSet = new QueueSystemWebSvc.QueueStatusDataSet();
        QueueSystemWebSvc.QueueStatusRequestDataSet queueStatusRequestDataSet = new QueueSystemWebSvc.QueueStatusRequestDataSet();
    
        QueueSystemWebSvc.QueueStatusRequestDataSet.StatusRequestRow statusRequestRow = queueStatusRequestDataSet.StatusRequest.NewStatusRequestRow();
        statusRequestRow.JobGUID = jobGuid;
        statusRequestRow.JobGroupGUID = Guid.NewGuid();
        statusRequestRow.MessageType = -1;
        queueStatusRequestDataSet.StatusRequest.AddStatusRequestRow(statusRequestRow);
    
        // Variables locales para usar
        bool inProcess = true;
        DateTime startTime = DateTime.Now;
        int successState = (int)PSLibrary.QueueConstants.JobState.Success;
        int failedState = (int)PSLibrary.QueueConstants.JobState.Failed;
        int blockedState = (int)PSLibrary.QueueConstants.JobState.CorrelationBlocked;
    
        // continuar mientras se esta procesando
        while (inProcess)
        {
            try
            {
                // Leer y obtener el estatus del job en projectServer
                queueStatusDataSet = m_queueSystem.ReadJobStatus(queueStatusRequestDataSet, false, QueueSystemWebSvc.SortColumn.Undefined, QueueSystemWebSvc.SortOrder.Undefined);
            }
            catch (Exception e)
            {
                throw e;
            }
    
            // Recorrer todas las filas obtenidas con los estatus
            foreach (QueueSystemWebSvc.QueueStatusDataSet.StatusRow statusRow in queueStatusDataSet.Status)
            {
                // Si existe algun error se muestra su excepcion
                if ((statusRow["ErrorInfo"] != System.DBNull.Value
                    && checkStatusRowHasError(statusRow["ErrorInfo"].ToString()) == true)
                    || statusRow.JobCompletionState == blockedState
                    || statusRow.JobCompletionState == failedState)
                {
                    inProcess = false;
                    throw (new ApplicationException("Queue request failed", new Exception(statusRow.ErrorInfo)));
                }
    
                // Si el estatus es correcto, se para el proceso de jobs para salir de la funcion
                if (statusRow.JobCompletionState == successState)
                {
                    inProcess = false;
                }
    
                // Hacer una pausa y seguir procesando jobs
                else
                {
                    System.Threading.Thread.Sleep(500);
                }
            }
        }
    }

    No intentes analizar esta ultima funcion ahora al principio, ya que hay que saber como funciona el PSI para entenderla.

    En cualquier caso esto te debe servir para tu proposito, pero si necesitas mas ayuda comentamelo.








    Saludos
    David González
    • Marcado como respuesta piskui martes, 05 de enero de 2010 14:33
    martes, 05 de enero de 2010 8:31
  • Hola David, muchas gracias por tu aporte la verdad es que me ha aclarado algo jeje.

    Como bien dices me ha tocado buen tema jejeje project y sharepoint, ambas tecnologias son nuevas para mi y es un poco duro al principio ya se sabe. Voy a bajarme la SDK y trastear, seguiré el primer codigo que me pusiste para crear el proyecto pero tengo un par de dudas:
    - En esa función no está implementado el nombre del servidor, usuario y autenticación verdad? (creo que con windows me vale).
    - Y otra cosa sabes como publicar el proyecto? no sé muy bien la diferencia entre crear y publicar pero me han dicho que primero debo crear un proyecto y después publicarlo.

    Cuando tenga esto funcionando tengo que hacer uso de lookuptable para insertar un registro en una tabla de búsqueda, no sé si sabrás como hacerlo, si no cuando llegue el momento, si no te importa te preguntaré por que todo de golpe...ando perdido sólo con los conceptos jeje.

    Lo dicho muchisimas gracias David, un saludo.


    piskui
    martes, 05 de enero de 2010 8:52
  • Ya he conseguido crear un proyecto y publicarlo he tenido algun problema con los customs fields necesarios ya que no me aparecían en el dataset que mandaba en el createproyect...
    Al final lo he 'solucionado' marcando como necesarios = no. No sé si tu David podrás darme una solución para esto o las indicaciones de como puedo hacerlo.

    Lo siguiente es crear el area de trabajo de ese proyecto, en el codigo de ejemplo del SDK hay un ejemplo llamado WorkSpaceTest que supongo que sea el area de trabajo pero no tiene formularios es sólo código....
    piskui
    martes, 05 de enero de 2010 14:35
  • hola de nuevo piskui.

    de project te puedo poner ejemplos de casi todo ya que llevo programando casi un par de años con el y sharepoint (entre otras cosas). Para solucionar lo anterior debes de obtener los customFields y lookupTables, ahora mismo me tengo que ir, pero a ver si te puedo explicar algo en los proximos dias poco a poco para que lo vayas implementando todo.

    En las funciones no esta el logon por que me dijiste que lo tenias realizado, existen varias formas y otras para poder suplantar usuario 'impersonate' pero es otro tema.
    Como te digo, vas a tener que aprenderlo con mucha paciencia y poco a poco.


    Saludos
    David González
    martes, 05 de enero de 2010 17:12
  • Hola David que tal?

    Si el logon lo tengo solucionado, de hecho ya creo y publico el proyecto por código. Lo siguiente es crear el area de trabajo de ese proyecto, que tengo un nuevo problema....

    Ya solucioné mi anterior problema, ahora debo meter por código un campo en una tabla de busqueda (lookuptable). ES decir indicando un nombre, insertar en la tabla clientes ese nombre, tienes algun ejemplo para orientarme? el método es este pero el ejemplo del MSDN es para delete no para update ya que quiero insertar no eliminar ningun registro...

    Si puedes mandarme tu email (no sé si hay mensaje privado en este foro) por si tengo alguna duda y podrías ayudarme con ella, si no es mucha molestia claro.

    Respecto a lo del tiempo y la paciencia, ya veo que es algo complejo y lo necesito pero las empresas ya se saben que el tiempo no sobra y paciencia tienen poca jeje.

    Gracias!
    piskui
    jueves, 07 de enero de 2010 7:21
  • Hola piskui.

    He tardado un poco, pero entre las fiestas y el trabajo he tenido mucho lio.

    Bien, no tengo mucho tiempo, asi que te voy a poner un poco de codigo que tengo para realizar tareas con campos personalizados y algo de tablas de busqueda, le echas un vistazo y si tienes algun problema o duda me lo comentas.

    Creo que no hay mensajes privados (al menos no los he visto nunca), pero puedes ir posteando tus dudas en este hilo, ya que me llegan las alertas y las puedo leer, de paso, le puede servir a mas gente la ayuda.

    Bien, aqui meto una clase con algunas funciones que seguro te serviran de guia:


    using System;
    using System.Data;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data.SqlClient;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Net;
    using System.Xml;
    using System.IO;
    using System.Linq;
    using System.Configuration;
    using System.Security.Cryptography.X509Certificates;
    using System.Web.Services.Protocols;
    
    using PSLibrary = Microsoft.Office.Project.Server.Library;
    
    /// <sumary>
    /// Nombre de espacio que contiene clases para operarciones con project server.
    /// </sumary> 
    namespace Zelion.Project
    {
        /// <sumary>
        /// Clase para realizar tareas con campos personalizados y tablas de consulta en project server 2007
        /// mediante el uso del PSI.
        /// </sumary>   
        public class CustomFields
        {
            #region [enumeraciones]
    
            /// <summary>Enumeracion con los tipos de entidad de los campos personalizados 'CustomFields'</summary>
            public enum ENT_TYPE
            {
                /// <summary>tipo de entidad para campos personalizados de tipo proyecto</summary>
                project = 1,
                /// <summary>tipo de entidad para campos personalizados de tipo tarea</summary>
                task,
                /// <summary>tipo de entidad para campos personalizados de tipo recurso</summary>
                resource
            };
    
            /// <summary>Enumeracion con los tipos de campos personalizados 'CustomFields' disponibles en project server 2007</summary>
            public enum TYPE_CUSTOMFIELD
            {
                /// <summary>tipo de campo personalizado de costo</summary>
                COST = PSLibrary.CustomField.Type.COST,
                /// <summary>tipo de campo personalizado de fecha</summary>
                DATE = PSLibrary.CustomField.Type.DATE,
                /// <summary>tipo de campo personalizado de duracion</summary>
                DURATION = PSLibrary.CustomField.Type.DURATION,
                /// <summary>tipo de campo personalizado de fecha final</summary>
                FINISHDATE = PSLibrary.CustomField.Type.FINISHDATE,
                /// <summary>tipo de campo personalizado de flag</summary>
                FLAG = PSLibrary.CustomField.Type.FLAG,
                /// <summary>tipo de campo personalizado de numero</summary>
                NUMBER = PSLibrary.CustomField.Type.NUMBER,
                /// <summary>tipo de campo personalizado de texto</summary>
                TEXT = PSLibrary.CustomField.Type.TEXT,
            };
    
            #endregion [enumeraciones]
    
    
            #region [funciones privadas]
    
            /// <summary>
            /// Funcion para crear e inicializar un objeto CustomFields del servicio web 'WebSvcCustomFields' que ofrece project server.
            /// Esta funcion asigna la url y las credenciales de acceso al servicio.
            /// </summary>
            /// <returns>Objeto CustomFields del servicio web 'WebSvcCustomFields' con las credenciales y la url establecida</returns>
            private WebSvcCustomFields.CustomFields getWebSvcCustomFields()
            {
                // Crear un objeto para acceder al servicio web custom fields.            
                WebSvcCustomFields.CustomFields customFields = new WebSvcCustomFields.CustomFields(); 
    
                // Asignar la url del servicio web 'WebSvcCustomFields'
                customFields.Url = "rutaServidor" + "/_vti_bin/PSI/CustomFields.asmx";
                // Asignar las credenciales para la conexion.
                customFields.Credentials = new NetworkCredential("UserName", "password", "domain");
    
                // retornar el objeto creado con las credenciales establecidas
                return customFields;
            }
    
            /// <summary>
            /// Funcion para crear e inicializar un objeto LookupTables del servicio web 'WebSvcLookupTable' que ofrece project server.
            /// Esta funcion asigna la url y las credenciales de acceso al servicio.
            /// </summary>
            /// <returns>Objeto LookupTables del servicio web 'WebSvcLookupTable' con las credenciales y la url establecida</returns>
            private WebSvcLookupTable.LookupTable getWebSvcLookupTable()
            {
                // Crear un objeto para acceder al servicio web 'WebSvcLookupTable'            
                WebSvcLookupTable.LookupTable LookupTable = new WebSvcLookupTable.LookupTable(); 
                // Asignar la url del servicio web 'WebSvcCustomFields'
                LookupTable.Url = "rutaServidor" + "/_vti_bin/PSI/LookupTable.asmx";
                // Asignar las credenciales para la conexion.
                LookupTable.Credentials = new NetworkCredential("UserName", "password", "domain");
    
                // retornar el objeto creado con las credenciales establecidas
                return LookupTable;
            }
    
            /// <summary>
            /// Funcion para obtener el error generado por project para las excepciones 'SoapException'
            /// </summary>
            /// <param name="_exception">SoapException generada en un bloque try catch</param>
            /// <returns>Mensaje completo del error que genera project server</returns>
            public string getErrorSoapException(SoapException _exception)
            {
                // variables para obtener el error
                string errAttributeName;
                string errAttribute;
                string errMess = "".PadRight(30, '=') + "\r\n" + "Error: " + "\r\n";
    
                // crear los objetos para obtener el error
                PSLibrary.PSClientError error = new PSLibrary.PSClientError(_exception);
                PSLibrary.PSErrorInfo[] errors = error.GetAllErrors();
                PSLibrary.PSErrorInfo thisError;
    
                // formar toda la cadena del error
                for (int i = 0; i < errors.Length; i++)
                {
                    thisError = errors[i];
                    errMess += "\n" + _exception.Message.ToString() + "\r\n";
                    errMess += "".PadRight(30, '=') + "\r\nPSCLientError Output:\r\n \r\n";
                    errMess += thisError.ErrId.ToString() + "\n";
    
                    // Añadir todos los atributos del error
                    for (int j = 0; j < thisError.ErrorAttributes.Length; j++)
                    {
                        errAttributeName = thisError.ErrorAttributeNames()[j];
                        errAttribute = thisError.ErrorAttributes[j];
                        errMess += "\r\n\t" + errAttributeName +
                                   ": " + errAttribute;
                    }
                    errMess += "\r\n".PadRight(30, '=');
                }
    
                // retornar el mensaje completo del error
                return errMess;
    
            }
    
            #endregion [funciones privadas]
    
    
            #region [funciones publicas]
    
            /// <summary>
            /// Funcion para obtener todos los campos personalizados (CustomFields) de project server
            /// dados por el filtro de entrada.
            /// La columna 'MD_PROP_UID' es la clave principal de la tabla y siempre sera retornada
            /// </summary>
            /// <param name="_filter">
            /// Filtro de la API de project para obtener los campos y registros deseados, se puede establecer a null para obtener
            /// todos los campos y registros.
            /// Para ver un ejemplo de uso de filtro mirar la funcion <see cref="GetCustomFieldsByEntity"/>.
            /// </param>
            /// <returns>
            /// Tabla con los customFields obtenidos dados por el filtro.        
            /// </returns>
            public DataTable GetCustomFields(PSLibrary.Filter _filter)
            {
                // no realizar un autoCheckOut
                bool autoCheckOut = false;
    
                // Crear un objeto para acceder al servicio web custom fields.
                WebSvcCustomFields.CustomFields customFields = getWebSvcCustomFields();
    
                try
                {
                    // Obtener la tabla con los campos personalizados y retornarlos (dependiendo del filtro)
                    if (null == _filter)
                        return customFields.ReadCustomFields("", autoCheckOut).CustomFields;
                    else
                        // El metodo 'GetXml' crea el parametro xmlFilter para la funcion 'ReadCustomFields'
                        return customFields.ReadCustomFields(_filter.GetXml(), autoCheckOut).CustomFields;
    
                }
                catch (System.Web.Services.Protocols.SoapException ex)
                {
                    throw new Exception(this.getErrorSoapException(ex));
                }
                catch (System.Net.WebException e)
                {
                    throw e;
                }
            }
    
            /// <summary>
            /// Funcion para obtener los campos personalizados (CustomFields) de los GUID pasados como 
            /// parametros
            /// </summary>
            /// <param name="_guids">Array de cadenas con los GUID's de los CustomFields que se quiere obtener</param>
            /// <returns>
            /// Si tiene exito, retorna una tabla con todas las columnas de los CustomFields obtenidos.        
            /// En caso contrario, retorna null.
            /// </returns>
            public DataTable GetCustomFieldsByGuid(string[] _guids)
            {
                // si no existen guid's se retorna null
                if (null == _guids) return null;
    
                // Obtener todos los campos personalizados, (se obtienen todos y luego se filtran ya que si se hace por filtro xml,
                // se deben especificar todas las columnas explicitamente, con lo que no merece la pena)
                string filter = "";
                for (int a = 0; a < _guids.Length; a++)
                {
                    filter += "MD_PROP_UID = '" + _guids[a] + "' OR ";
                }
                filter = filter.Remove(filter.Length - 4);
    
                DataTable result = GetCustomFields(null);
                result.DefaultView.RowFilter = filter;
    
                // retornar la tabla filtrada
                return result.DefaultView.ToTable();
            }
    
            /// <summary>
            /// Funcion para Obtener campos personalizados (Custom Fields) para una entidad especificada de proyect server,
            /// pudiendo ademas filtrar las columnas y ordenar una de ellas.
            /// </summary>
            /// <param name="_entType">Enumeracion 'ENT_TYPE' con el tipo de entidad.</param>
            /// <param name="_fieldOrder">
            /// Nombre del campo que sera ordenado segun el criterio del parametro _sortOrder.
            /// </param>
            /// <param name="_sortOrder">Enumeracion 'PSLibrary.Filter.SortOrderTypeEnum' con el orden para la columna del nombre 'MD_PROP_NAME'</param>
            /// <param name="_filterFields">
            /// array con los campos que se desean obtener mediante el filtrado, si este valor es null, se obtendran todos los campos.
            /// Los posibles campos se pueden consultar en la SDK mediante 'CustomFields' o ver con intelissense del modo siguiente:
            /// WebSvcCustomFields.CustomFieldDataSet cfDataSet = new WebSvcCustomFields.CustomFieldDataSet();
            /// cfDataSet.CustomFields.MD_PROP_NAMEColumn.ColumnName;
            /// ...
            /// </param>
            /// <returns>
            /// En caso de tener exito, retorna un dataTable con los customFields encontrados.
            /// en caso contrario, retorna null.
            /// </returns>
            public DataTable GetCustomFieldsByEntity(ENT_TYPE _entType, PSLibrary.Filter.SortOrderTypeEnum _sortOrder, string _fieldOrder,
                string[] _filterFields)
            {
                // Si no existen campos a filtrar se retorna null
                if (null == _filterFields) return null;
    
                // Crear un objeto para acceder al servicio web custom fields.
                WebSvcCustomFields.CustomFields customFields = new WebSvcCustomFields.CustomFields();
    
                // Obtener el ID unico del tipo de entidad especificado.
                Guid entityUid = Guid.Empty;
                switch (_entType)
                {
                    case ENT_TYPE.project:
                        entityUid = new Guid(PSLibrary.EntityCollection.Entities.ProjectEntity.UniqueId);
                        break;
                    case ENT_TYPE.task:
                        entityUid = new Guid(PSLibrary.EntityCollection.Entities.TaskEntity.UniqueId);
                        break;
                    case ENT_TYPE.resource:
                        entityUid = new Guid(PSLibrary.EntityCollection.Entities.ResourceEntity.UniqueId);
                        break;
                }
    
                // Datasset para establecer los filtros
                WebSvcCustomFields.CustomFieldDataSet cfDataSet = new WebSvcCustomFields.CustomFieldDataSet();
                // Obtener el nombre de la tabla y el nombre de la columna que define la entidad para filtrar
                string tableName = cfDataSet.CustomFields.TableName;
                string entityUidColumnName = cfDataSet.CustomFields.MD_ENT_TYPE_UIDColumn.ColumnName;
    
                // Definir un operador para el filtro, comparar un campo con datos.
                PSLibrary.Filter.FieldOperationType equal = PSLibrary.Filter.FieldOperationType.Equal;
                // Crear un objeto para filtrar
                PSLibrary.Filter cfFilter = new PSLibrary.Filter();
                // Restringir el filtro a una tabla ( solo en el caso de CustomFieldsDataSet )
                cfFilter.FilterTableName = tableName;
    
                // Si existe un campo a ordenar, se añade al filtro
                if (!string.IsNullOrEmpty(_fieldOrder))
                    cfFilter.Fields.Add(new PSLibrary.Filter.Field(tableName, _fieldOrder, _sortOrder));
    
                // recorrer todos los posibles campos a filtrar para añadirlos al filtro
                for (int a = 0; a < _filterFields.Length; a++)
                {
                    cfFilter.Fields.Add(new PSLibrary.Filter.Field(_filterFields[a]));
                }
    
    
                // Usar un criterio condicional para las filas que seran retornadas
                // se puedes usar multiples juegos de operadores, y conectar las condiciones con operadores logicos
                // En este caso, el campo MD_ENT_TYPE_UID debe tener un valor igual al 'entityUid. '
                cfFilter.Criteria = new PSLibrary.Filter.FieldOperator(equal, entityUidColumnName, entityUid);
    
                // Obtener los campos personalizados dados por el filtro creado y retornarlos
                return GetCustomFields(cfFilter);
            }
    
            /// <summary>
            /// Funcion para crear un campo personalizado 'customField' de project server, con posibilidad de asociarlo
            /// a una tabla de consulta dada por su guid.
            /// </summary>
            /// <param name="_name">Nombre del campo personalizado</param>
            /// <param name="_entType">tipo de entidad donde sera asociado el customField dado por la enumeracion 'ENT_TYPE'</param>
            /// <param name="_type">tipo de customField a crear dado por la enumeracion 'TYPE_CUSTOMFIELD'</param>
            /// <param name="_guidLookupTable">
            /// guid de la tabla de consulta a asociar 'LookupTable', o null en caso de no querer asociarlo a ninguna tabla de consulta
            /// </param>
            /// <returns>guid del sustomField creado correctamente</returns>
            public string createCustomField(string _name, ENT_TYPE _entType, TYPE_CUSTOMFIELD _type, string _guidLookupTable)
            {
                // crear el dataset para los customfields
                WebSvcCustomFields.CustomFieldDataSet dsCustomFields = new WebSvcCustomFields.CustomFieldDataSet();
                // crear una fila con el customfield para añadir al dataset
                WebSvcCustomFields.CustomFieldDataSet.CustomFieldsRow cfRow = dsCustomFields.CustomFields.NewCustomFieldsRow();
    
                // Obtener el ID unico del tipo de entidad especificado.
                Guid entityUid = Guid.Empty;
                switch (_entType)
                {
                    case ENT_TYPE.project:
                        entityUid = new Guid(PSLibrary.EntityCollection.Entities.ProjectEntity.UniqueId);
                        break;
                    case ENT_TYPE.task:
                        entityUid = new Guid(PSLibrary.EntityCollection.Entities.TaskEntity.UniqueId);
                        break;
                    case ENT_TYPE.resource:
                        entityUid = new Guid(PSLibrary.EntityCollection.Entities.ResourceEntity.UniqueId);
                        break;
                }
    
                // asignar las propiedades del customfield                                                
                cfRow.MD_PROP_UID = Guid.NewGuid();
                cfRow.MD_PROP_NAME = _name;
                cfRow.MD_ENT_TYPE_UID = entityUid;
                cfRow.MD_PROP_TYPE_ENUM = Convert.ToByte(_type);
    
                // si existe una tabla de consulta asociada, se añade su guid
                if (!string.IsNullOrEmpty(_guidLookupTable))
                    cfRow.MD_LOOKUP_TABLE_UID = new Guid(_guidLookupTable);
    
                // añadir la fila al dataset de customFields para crearlo posteriormente
                dsCustomFields.CustomFields.Rows.Add(cfRow);
    
                // crear el objeto para crear los customFields
                WebSvcCustomFields.CustomFields customFields = getWebSvcCustomFields();
                try
                {
                    // crear el customfield y realizando auto CheckIn
                    customFields.CreateCustomFields(dsCustomFields, false, true);
                }
                catch (SoapException ex)
                {
                    throw new Exception(this.getErrorSoapException(ex));
                }
    
                // retornar el guid del customField
                return cfRow.MD_PROP_UID.ToString();
            }
    
            /// <summary>
            /// Funcion para borrar uno o varios customFields dados por sus guids en el parametro de entrada
            /// </summary>
            /// <param name="_guids">
            /// array de guids de los customFields que se desean borrar.
            /// el guid viene dado por la columna 'MD_PROP_UID' de los customFields
            /// </param>
            public void deleteCustomField(string[] _guids)
            {
                // obtener todos los guid, comprobando que sean correctos
                List<Guid> guids = new List<Guid>();
                for (int a = 0; a < _guids.Length; a++)
                {
                    try
                    {
                        // intentar crear el guid y asignarlo al array
                        Guid guid = new Guid(_guids[a]);
                        guids.Add(new Guid(_guids[a]));
                    }
                    catch (Exception) { }
                }
    
                // crear el objeto para borrar los customFields
                WebSvcCustomFields.CustomFields customFields = getWebSvcCustomFields();
                try
                {
                    // hacer un checkout para poder borrar los customFields
                    customFields.CheckOutCustomFields(guids.ToArray());
                    // borrar los customFields con guid valido
                    customFields.DeleteCustomFields(guids.ToArray());
                }
                catch (SoapException ex)
                {
                    throw new Exception(this.getErrorSoapException(ex));
                }
    
            }
    
            /// <summary>
            /// Funcion para añadir el valor de un campo personalizado a un proyecto con o sin tabla de consulta (lookupTable)
            /// </summary>
            /// <param name="_customField">Campo personalizado (customField) deseado</param>
            /// <param name="_value">
            /// Valor a asignar al campo personalizado (dependiendo del tipo), si tiene una LookupTable, se debe especificar el GUID
            /// de la estructura del valor (columna 'LT_STRUCT_UID'), en caso de no tener una LookupTable, 
            /// se debe especificar el valor deseado dependiendo del tipo de customField (Texto, numero, fecha, etc.)
            /// </param>
            /// <param name="_projectGuid">Proyecto donde sera añadido el CustomField</param>
            /// <returns>guid del valor añadido para el customField, null si no ha tenido exito</returns>
            public string addProjectCustomField(DataRow _customField, string _value, Guid _projectGuid)
            {
                // crear el objeto de acceso al servicio web para el proyecto
                ProjectWebSvc.Project project = new ProjectWebSvc.Project(); 
    
                // Crear un guid para la sesion y para la cola de project
                Guid sessionGuid = Guid.NewGuid();
                Guid jobGuid = Guid.NewGuid();
                bool flagCheckOut = false;
    
                try
                {
    
                    // DataSet y fila para crear la Asociacion del Custom field al proyecto
                    ProjectWebSvc.ProjectDataSet addProjCF = new ProjectWebSvc.ProjectDataSet();
                    ProjectWebSvc.ProjectDataSet.ProjectCustomFieldsRow newCFrow = addProjCF.ProjectCustomFields.NewProjectCustomFieldsRow();
    
                    // Crear el registro del CustomField a añadir
                    newCFrow.SetNUM_VALUENull();
                    newCFrow.SetFLAG_VALUENull();
                    newCFrow.SetDUR_VALUENull();
                    newCFrow.SetDUR_FMTNull();
                    newCFrow.SetDATE_VALUENull();
                    newCFrow.SetCODE_VALUENull();
                    newCFrow.SetTEXT_VALUENull();
    
                    newCFrow.MD_PROP_UID = new Guid(_customField["MD_PROP_UID"].ToString());
                    newCFrow.MD_PROP_ID = int.Parse(_customField["MD_PROP_ID"].ToString());
                    newCFrow.CUSTOM_FIELD_UID = Guid.NewGuid();
                    newCFrow.PROJ_UID = _projectGuid;
                    newCFrow.FIELD_TYPE_ENUM = byte.Parse(_customField["MD_PROP_TYPE_ENUM"].ToString());
    
                    // evaluar el tipo de campo y actuar en consecuencia
                    switch (newCFrow.FIELD_TYPE_ENUM)
                    {
                        case (byte)PSLibrary.CustomField.Type.COST:
                            newCFrow.NUM_VALUE = Convert.ToInt32(_value);
                            break;
    
                        case (byte)PSLibrary.CustomField.Type.DATE:
                            newCFrow.DATE_VALUE = Convert.ToDateTime(_value);
                            break;
    
                        case (byte)PSLibrary.CustomField.Type.DURATION:
                            newCFrow.DUR_VALUE = Convert.ToInt32(_value);
                            break;
    
                        case (byte)PSLibrary.CustomField.Type.FINISHDATE:
                            newCFrow.DATE_VALUE = Convert.ToDateTime(_value);
                            break;
    
                        case (byte)PSLibrary.CustomField.Type.FLAG:
                            newCFrow.FLAG_VALUE = Convert.ToBoolean(_value);
                            break;
    
                        case (byte)PSLibrary.CustomField.Type.NUMBER:
                            newCFrow.NUM_VALUE = Convert.ToInt32(_value);
                            break;
    
                        case (byte)PSLibrary.CustomField.Type.TEXT:
                            if (_customField["MD_LOOKUP_TABLE_UID"] != null && _customField["MD_LOOKUP_TABLE_UID"].ToString() != "")
                            {
                                newCFrow.CODE_VALUE = new Guid(_value);
                            }
                            else
                            {
                                newCFrow.TEXT_VALUE = _value;
                            }
                            break;
                    }
    
                    // añadir el registro al DataSet
                    addProjCF.ProjectCustomFields.AddProjectCustomFieldsRow(newCFrow);
    
                    // Realizar un CheckOut para subir los cambios                
                    project.CheckOutProject(_projectGuid, sessionGuid, "Checked out por Zelion para añadir CustomField");
                    flagCheckOut = true;
    
                    // Subir los cambios en ProjectServer                
                    project.QueueAddToProject(jobGuid, sessionGuid, addProjCF, false);
                    // esperar a que se ejecute la tarea de añadir al proyecto            
                    WaitForQueue(jobGuid);
    
                    // retornar el guid creado del customField añadido
                    return newCFrow.CUSTOM_FIELD_UID.ToString();
                }
                catch (SoapException ex)
                {
                    throw ex;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    if (flagCheckOut)
                    {
                        // Realizar un CheckIn para poder seguir usando el proyecto
                        project.QueueCheckInProject(jobGuid, _projectGuid, true, sessionGuid, "Checked in por Zelion para añadir CustomField");
                        // esperar a que se ejecute el checkIn          
                        WaitForQueue(jobGuid);
                    }
                }
            }
    
            /// <summary>
            /// Funcion para eliminar valores de CustomFields añadidos a un proyecto.
            /// NOTA: para poder ser eliminados, primero se deben eliminar los CustomFields de Projectserver<see cref="deleteCustomField"/>
            /// </summary>
            /// <param name="_projectCustomFieldGui">
            /// guid de la entidad del proyecto que tiene el valor del custom field.
            /// (Dataset del proyecto, tabla 'ProjectCustomFields' columna 'CUSTOM_FIELD_UID')
            /// </param>
            /// <param name="_projectGuid">GUID del proyecto del cual se eliminara la entidad del customField añadido</param>
            public void removeProjectCustomField(Guid[] _projectCustomFieldGui, Guid _projectGuid)
            {
                // crear el objeto de acceso al servicio web para el proyecto
                ProjectWebSvc.Project project = new ProjectWebSvc.Project(); 
    
                // Crear un guid para la sesion y para la cola de project
                Guid sessionGuid = Guid.NewGuid();
                Guid jobGuid = Guid.NewGuid();
                bool flagCheckOut = false;
    
                try
                {
                    // Realizar un CheckOut para realizar cambios en el proyecto
                    project.CheckOutProject(_projectGuid, sessionGuid, "Checked out por Zelion para borrar entidades CustomField en un proyecto");
                    flagCheckOut = true;
    
                    // Subir los cambios de borrado a ProjectServer                  
                    project.QueueDeleteFromProject(jobGuid, sessionGuid, _projectGuid, _projectCustomFieldGui);
                    // esperar a que se ejecute la tarea de borrar del proyecto            
                    WaitForQueue(jobGuid);
                }
                catch (SoapException ex)
                {
                    throw new Exception(this.getErrorSoapException(ex));
                }
                finally
                {
                    if (flagCheckOut)
                    {
                        // Realizar un CheckIn para poder seguir usando el proyecto
                        project.QueueCheckInProject(jobGuid, _projectGuid, true, sessionGuid,
                            "Checked in por Zelion para borrar entidades CustomField en un proyecto");
                        // esperar a que se ejecute el checkIn          
                        WaitForQueue(jobGuid);
                    }
                }
            }
    
            /// <summary>
            /// Funcion para obtener la informacion de las tablas de consulta de projectServer 'MPS_LOOKUP_TABLE_VALUES'
            /// </summary>
            /// <param name="_guids">
            /// Un array con los guid de las tablas de consulta a obtener, el guid corresponde con
            /// la columna 'LT_UID' de lockupTables y 'MD_LOOKUP_TABLE_UID' de CustomFields (entre otros)        
            /// </param>
            /// <returns>
            /// En caso de no tener exito retorna null.
            /// Si tiene exito, un dataset con 3 tablas, una con los datos de la looupTable, otra con los datos de las mascaras y otra
            /// que representa un arbol con los valores de la tabla donde el campo 'LT_PARENT_STRUCT_UID' define el nodo padre
            /// del arbol y si este es null, es el nodo raiz
            /// </returns>        
            public DataSet GetLookupTablesByGuid(string[] _guids)
            {
                // si el _guid esta vacio, se retorna un datatable vacio
                if (null == _guids) return null;
    
                try
                {
                    // Crear un objeto para acceder al servicio web 'WebSvcLookupTable'
                    WebSvcLookupTable.LookupTable LookupTable = getWebSvcLookupTable();
    
                    // obtener todos los guid, comprobando que sean correctos
                    List<Guid> guids = new List<Guid>();
                    for (int a = 0; a < _guids.Length; a++)
                    {
                        try
                        {
                            // intentar crear el guid y asignarlo al array
                            Guid guid = new Guid(_guids[a]);
                            guids.Add(new Guid(_guids[a]));
                        }
                        catch (Exception) { }
                    }
    
                    // Obtener y retornar el dataset con las tablas con informacion de la tabla de consulta 'LooupTables'.
                    // (Lenguaje: 0x0C0A = español)
                    return (DataSet)LookupTable.ReadLookupTablesByUids(guids.ToArray(), false, 0x0C0A);
                }
    
                // Control errores
                catch (System.Web.Services.Protocols.SoapException ex)
                {
                    throw new Exception(this.getErrorSoapException(ex));
                }
            }
    
            /// <summary>
            /// Funcion para obtener todas las tablas de consulta (LookupTables) de project server 'MPS_LOOKUP_TABLE_VALUES'
            /// dados por el filtro de entrada.        
            /// </summary>
            /// <param name="_filter">
            /// Filtro de la API de project para obtener los campos y registros deseados, se puede establecer a null para obtener
            /// todos los campos y registros.
            /// Para ver un ejemplo de uso de filtro mirar la funcion <see cref="GetCustomFieldsByEntity"/>.
            /// </param>        
            /// Si tiene exito, un dataset con 3 tablas, una con los datos de la looupTable, otra con los datos de las mascaras y otra
            /// que representa un arbol con los valores de la tabla donde el campo 'LT_PARENT_STRUCT_UID' define el nodo padre
            /// del arbol y si este es null, es el nodo raiz
            public DataSet GetLookupTables(PSLibrary.Filter _filter)
            {
                // no realizar un autoCheckOut
                bool autoCheckOut = false;
    
                // Crear un objeto para acceder al servicio web 'WebSvcLookupTable'
                WebSvcLookupTable.LookupTable LookupTable = getWebSvcLookupTable();
    
                try
                {
                    // Obtener el dataset con la informacion de las tablas de consulta y retornarlos (dependiendo del filtro)
                    // (Lenguaje: 0x0C0A = español)
                    if (null == _filter)
                        return LookupTable.ReadLookupTables("", autoCheckOut, 0x0C0A);
                    else
                        // El metodo 'GetXml' crea el parametro xmlFilter para la funcion 'ReadLookupTables'
                        // NOTA: para el filtro, no se puede especificar lenguaje y se debe establecer en 0
                        return LookupTable.ReadLookupTables(_filter.GetXml(), autoCheckOut, 0);
    
                }
                catch (System.Web.Services.Protocols.SoapException ex)
                {
                    throw new Exception(this.getErrorSoapException(ex));
                }
                catch (System.Net.WebException e)
                {
                    throw e;
                }
            }
    
    
            #endregion [funciones publicas]
        }
    }
    



    He modificado algo de codigo para dejar solo estas funciones, si no te funciona algo o tienes dudas me lo comentas.



    Saludos 
    David González

    lunes, 11 de enero de 2010 17:14