none
CSOM - Acessing and updating Project Server 2013 timephased data RRS feed

  • Question

  • I'm seeking the CSOM method to acess and update Project Server 2013 timephased data using .NET managed assemblies. Any sample code provided would be greatly appreciated.
    Thursday, July 10, 2014 8:06 PM

All replies

  • Hi Edward,

    I'm in the process of writing an application which has to update timephased data in CSOM. Of course I can not give you all the code, but you will find below the hearth of the Update Process.


    In the sample code below, I send an entity which contains all the data I want to update: Achieved Time, Planned Time, Day, etc...I guess it will be easy to understand.

    The code should work online or on premise. Depending of your context, youu will have to managed authentification differently.

    I hope I will make you save some time, because it's not really well documented.

    BR,

    @SylvainGrossNeo


           /// <summary>
            /// Principe:
            /// L'entité timeSheetEntity possède les attributs qui permettent de créer un StatusAssignment
            /// </summary>
            /// <param name="timesheetEntity"></param>
            public static void SaveTimeSheet(TimeSheetEntity timesheetEntity)
            {
                try
                {
                    projContext = new ProjectContext(PwaPath);
                    if (IsProjectOnline)
                        projContext.ExecutingWebRequest += ClaimsHelper.clientContext_ExecutingWebRequest;
                    else
                        projContext.Credentials = new System.Net.NetworkCredential("XXXX", "YYY", "ZZZZ");
    
                    projContext.Load(projContext.TimeSheetPeriods, c => c.Where(p => p.Start <= DateTime.Now && p.End >= DateTime.Now).
                        IncludeWithDefaultProperties(p => p.TimeSheet,
                                                        p => p.TimeSheet.Lines.Where(l => l.ProjectName != "Administrative").
                        IncludeWithDefaultProperties(l => l.Assignment,
                                                        l => l.Assignment.Task,
                                                        l => l.Work)));
    
                    projContext.ExecuteQuery();
    
    
    
                    var maPeriod = projContext.TimeSheetPeriods.FirstOrDefault();
    
                    TimeSheetWorkCreationInformation workCreation = new TimeSheetWorkCreationInformation
                    {
    
                        ActualWork = string.Format("{0}h", timesheetEntity.AchievedTime),
                        Start = timesheetEntity.Day,
                        End = timesheetEntity.Day,
                        Comment = timesheetEntity.Comment,
                        NonBillableOvertimeWork = "0",
                        NonBillableWork = "0",
                        OvertimeWork = "0",
                        PlannedWork = string.Format("{0}h", timesheetEntity.PlannedTime)
                    };
    
                    var line = maPeriod.TimeSheet.Lines.Where(l => l.Id == timesheetEntity.UId).FirstOrDefault();
    
    
                    line.Work.Add(workCreation);
    
                    maPeriod.TimeSheet.Update();
    
                    
    
                    if (maPeriod.TimeSheet.Status == TimeSheetStatus.Approved ||
                        maPeriod.TimeSheet.Status == TimeSheetStatus.Submitted ||
                        maPeriod.TimeSheet.Status == TimeSheetStatus.Rejected)
                    {
                        maPeriod.TimeSheet.Recall();
                    }
    
                    maPeriod.TimeSheet.Submit("GO");
    
                    projContext.ExecuteQuery();
    
                }
    

    Friday, July 11, 2014 1:47 PM
  • Hi Sylvain,

    I was looking at this method to use the TimePhase object:

    foreach (PublishedAssignment assignItem in task.Assignments)

    {

        projContext.Load(assignItem.Resource);

        projContext.ExecuteQuery();

     

        EnterpriseResource er = projContext.EnterpriseResources.GetByGuid(assignItem.Resource.Id);

     

        StatusAssignmentCollection sac = er.Assignments;

     

        TimePhase tp = sac.GetTimePhase(new DateTime(2014, 1, 1), new DateTime(2014, 12, 31));

     

    ---

    Are you familiar with this approach? The TimePase object properties are currently returning null.  

    Thanks,

    Ed

    Tuesday, July 15, 2014 10:46 PM
  • Hi Ed,

    No I never used the timephase class in my developments. I cannot see any doc on this class.
    May I recommand you to start with the approach  I proposed, the time that you get familiar with CSOM ?
    If you want to continue with your approach, you should consider to load more properties in your query,
    with the method IncludeWithDefaultProperties, as shown in my code

    You should load your assignments. Remember that by default, CSOM loads the minimum number of properties, to reduce the workload.
    If you want yo load more property, add the IncludeWithDefaultProperties clause in,your Linq queries, and it should be better !
    Regards,
    Sylvain

    Wednesday, July 16, 2014 9:17 PM
  • Hi Edward,

    I don't know if you managed to solve your problem, but I've just finish to publish in the Technet Gallery an simple C# application showing exactly the process to:

    • Read Project and Tasks
    • Create Tasks
    • Create Timephased Assignment
    • Create actuals in the Timesheet

    I think it should be a good point to start for you, and maybe others.

    I'm back from holidays, so don't hesitate to ask me !

    http://gallery.technet.microsoft.com/OnlineServer-2013-e1950d29#content

    Regards,

    Sylvain

    Friday, August 1, 2014 12:58 PM
  • Hello Sylvain,

    We are a partially working solution for accessing and updating time phase data, but it’s currently only working for the authenticated user that is also the project owner. We are also limited in using the TimePhase class (no timesheets).

    The code currently works in submitting time phase data when the authenticated user is the project owner. It fails for every other user regardless of user permissions set in Project Server Online with an error: "PJClientCallableException: GeneralSecurityAccessDenied”.

    
    
    Our user authentication is based on SharePointOnlineCredentials. We are definitely missing a method to authenticate for users that are not project owners. 

    // Timephased data & status submittal

    EnterpriseResource er = projContext.EnterpriseResources.GetByGuid(resource.Id);
    projContext.Load(er.Assignments);
    projContext.ExecuteQuery();
    log.AppendFormat("<br />Getting time phase data for: {0}...", startDate.ToShortDateString());
    TimePhase tp = er.Assignments.GetTimePhase(startDate, endDate);
    projContext.Load(tp.Assignments);
    projContext.ExecuteQuery();  <- Error Here
    if (tp.Assignments.Count() > 0 && !isActualsExist){
        isActualsExist = true;
    }
    projContext.Load(tp.Assignments,
             assign => assign.IncludeWithDefaultProperties(a => a.Id).Where(a => a.Id == assn_uid));
    projContext.ExecuteQuery();
    log.Append("done");
    if (tp.Assignments.Count() > 0){
        decimal currentActualWork = 0;
        currentActualWork = Regex.Replace(tp.Assignments[0].ActualWork, "[^.0-9]""").ToDecimal();
        actualWork /= 60;
        actualWork += currentActualWork;
        tp.Assignments[0].ActualWork = actualWork.ToString("N4");
        tp.Assignments.Update();
        tp.Assignments.SubmitAllStatusUpdates(String.Format("Actuals submittals for {0} by "DateTime.Now));
    }

    Appreciate any input that you can provide.

    Regards,

    Edward


    Edward Lane

    Thursday, October 16, 2014 7:54 PM
  • Sylvain (the great guy that he is) was very generous in providing me with some input on this issue.  He felt that my issue is related to an issue with the O365 authentification protocol - "The solution was to subscribe on the WebRequest execution event, and to add a security token to this request.". I attempted to do this using the ClaimsHelper/MsHelperOnline library and as of yet have not gotten it to work. Still getting error PJClientCallableException: GeneralSecurityAccessDenied”.

    Edward Lane

    Monday, October 20, 2014 10:54 PM
  • Hi Ed,

    If the problem is not related to the security token, it should come from the application layer.
    Can you double check if the users have enough rights on Project ?

    If the Project Owners have the right of writing timephased data, and the other users can't, I should mean that the problem is more related to a authorization in your project instance, rather than a global access problem.

    I would suggest to change the right of the users, by putting them in the admin security group, to give them all the possible rights. It's possible that with the maximum right level, your script would run...

    Another check that you could do: try to execute different kind of CSOM method: getting the project list, the enterprise resources list, etc...If some method run, and some other don't, it would definitively mean that the problem is linked to the autorisation in Project, and not to a network/token problem as I initially thought.

    Regards,

    Tuesday, October 21, 2014 6:07 AM
  • Hi Sylvain,

    We've verified all project rights and all users that we're testing with have admin rights and have all rights on the project.

    This is what a Microsoft suport engineer wrote to us regarding said issue:

    We completed our research with CSOM and Remote Authentication and it is not going to do what you are expecting it to do. The behavior is however expected in the Authentication and authorization process, the user that you are connecting with is able to Authenticate, however, the SPUser object will not get the authorization to pull up something that’s not assigned to them.

    Regards,


    Edward Lane

    Thursday, October 23, 2014 6:21 PM