Editting TimeSheets with the PSI QueueUpdateTimesheet RRS feed

  • Question

  • Would some one who has been successful with editting TimeSheets via QueueUpdateTimesheet() help us out with a look at this code? 


    DateTime [] dates; // arrays are same size
    decimal [] actuals;
    Guid tsLineGuid;   // from existing TimeSheet

       SvcAdmin.TimesheetLineClassDataSet tsLineClassDs =
          adminClient.ReadLineClasses(SvcAdmin.LineClassType.AllNonProject, SvcAdmin.LineClassState.Enabled);
    // delta TS is the difference in previous to new timesheet SvcTimeSheet.TimesheetDataSet deltaTsDs = new SvcTimeSheet.TimesheetDataSet(); SvcTimeSheet.TimesheetDataSet.HeadersRow headersRow = deltaTsDs.Headers.NewHeadersRow(); headersRow.RES_UID = resourceGuid; headersRow.TS_UID = timeSheetGuid; headersRow.WPRD_UID = periodGuid; headersRow.TS_CREATOR_RES_UID = managerGuid; headersRow.TS_NAME = "Delta Timesheet " + periodStartDate + " to " + periodFinishDate; headersRow.TS_COMMENTS = "Delta Timesheet created by Web Part."; headersRow.TS_ENTRY_MODE_ENUM = (byte)PSLibrary.TimesheetEnum.EntryMode.Daily; deltaTsDs.Headers.AddHeadersRow(headersRow);
    SvcTimeSheet.TimesheetDataSet.LinesRow line = deltaTsDs.Lines.NewLinesRow(); line.TS_UID = timeSheetGuid; line.TS_LINE_UID = tsLineGuid; line.TS_LINE_CLASS_UID = tsLineClassDs.LineClasses[0].TS_LINE_CLASS_UID; line.TS_LINE_COMMENT = "Timesheet created by Web Part."; line.TS_LINE_STATUS = (byte)PSLibrary.TimesheetEnum.LineStatus.NotApplicable; line.TS_LINE_VALIDATION_TYPE = (byte)PSLibrary.TimesheetEnum.ValidationType.Unverified; line.TS_LINE_CACHED_ASSIGN_NAME = tsLineClassDs.LineClasses[0].TS_LINE_CLASS_DESC; deltaTsDs.Lines.AddLinesRow(line);
    // add actuals for (int i = 0; i < dates.Length; i++) { SvcTimeSheet.TimesheetDataSet.ActualsRow actual = deltaTsDs.Actuals.NewActualsRow(); actual.TS_LINE_UID = tsLineGuid; actual.TS_ACT_VALUE = actuals[i]; actual.TS_ACT_START_DATE = dates[i]; actual.TS_ACT_FINISH_DATE = dates[i]; deltaTsDs.Actuals.AddActualsRow(actual); }
    timeSheetClient.PrepareTimesheetLine(timeSheetGuid, ref deltaTsDs, new Guid[] { tsLineGuid }); timeSheetClient.QueueUpdateTimesheet(jobGuid, timeSheetGuid, deltaTsDs);
    } catch (SoapException ex) { throw new Exception(ex.Message); } catch (WebException ex) { throw new Exception(ex.Message); } catch (Exception ex) { throw new Exception(ex.Message); }

    The error is coming via a GridView Editing Javascript window:

    Message: Sys.WebForms.PageRequestManagerServerErrorException: ProjectServerError(s) LastError=GeneralItemDoesNotExist Instructions: Pass this into PSClientError constructor to access all error information
    Line: 4723
    Char: 21
    Code: 0
    URI: http://<my_url>/ScriptResource.axd?d=...
    The goal is to take delta hours and update an existing TimeSheet with them.  I tried using the tsLineGuid and timeSheetGuid from the existing TimeSheet and I tried creating them anew with Guid.NewGuid(); 

    Jonathan M Beck

    • Edited by Beta Star Friday, September 7, 2012 3:36 PM
    Tuesday, August 28, 2012 4:39 PM

All replies

  • No one answers my question.  We are paying a considerable amount for the Products we use and we have a Support account.

    I've looked around and seen mayn people having trouble with the Timesheet Update Queueing via the PSI.

    Can someone tell me why we must do this operation via a Queue, and not with a direct SQL operation using something like this Stored Procedure from the Reporting DataBase:


    Jonathan M Beck

    Thursday, August 30, 2012 5:54 PM
  • I have never done the above, but having worked with the PSI for quite some time I wonder about a couple of things. Firstly - given that you are updating an existing timesheet, I would not expect that you have to set headers. They should be set already. Secondly I see you adding a timesheetline, but it seems that you have already a guid. A new line requires a new guid. You already hinted in your final comments that you want to update an existing timesheet - the question is if it is an exisitng line or a new line.

    Jan Cirpka

    Monday, September 3, 2012 7:34 PM
  • Jan,

    Thanks for your answer.  I think what I was assuming was:

    1)  I have a TimeSheet and TimeSheet Guid it yields:

    2)  An existing Line with a Line GUID

    3)  Herein lies my confusion: Update the Line with new Actual value

    Considering that in most places in the PSDB where there is an "update" operation you use the existing GUID, I assumed that it was the same here.   But what you are telling me is that even though I already have a Line GUID, I need to create a new one for each edit. Therefore, 100 edits is 100 Line GUIDs and only the most recent will be pulled when I call timeSheetDS.Actuals.FindByTS_LINE_UIDTS_ACT_START_DATE()?

    • Edited by Beta Star Tuesday, September 4, 2012 1:13 PM
    Tuesday, September 4, 2012 1:11 PM
  • If you wish to update actuals on an existing timesheet with existing lines all you need to do is to add the actual lines. In your code sample above you added a new header and a new line - that caused the confusion.

    In fact, for a given timesheet with lines you will also have (potentially) actuals set. So you need to loop also through the existing actuals to see if you have to update the exiting value or to create a new actual line if not present.

    Jan Cirpka

    Wednesday, September 5, 2012 9:52 AM
  • Jan,

    I think I've got it now.  My old algorithm did something along the lines of: 

                    • I have the existing TimeSheet, let's call it TS1.
                    • My current algorithm adds a new Line to TS1, and then adds a new Actual to TS1.
                    • I create a new Guid jobGuid.
                    • I then call
                    • SvcTimesheet.PrepareTimesheetLine(ts1Guid, ref ts1, newLineGuid)
                    • SvcTimeSheet.QueueUpdateTimesheet(jobGuid, ts1Guid, ts1)

    At this point it throws an error, "GeneralObjectAlreadyExists".

    The function declaration for QueueUpdateTimesheet() says the third parameter is a delta Timesheet.  The algorithm should be:

    1. I have the existing TimeSheet, let's call it TS1. 
    2. I create a new timesheet called deltaTs, copying the header from TS1, except for the TS Guid which is unique.
    3. Add a new Line to deltaTs, and then add a new Actual to deltaTs.
    4. I create a new Guid jobGuid.
    5. I then call
    6. SvcTimesheet.PrepareTimesheetLine(ts1Guid, ref deltaTs, newLineGuid)
    7. SvcTimeSheet.QueueUpdateTimesheet(jobGuid, ts1Guid, deltaTs)

    It's a little embarassing to be so confused.  To call those functions you use the existing TS Guid, but the deltaTs.  And the actuals are treated more like historical entries rather than unique entries by PSDB.

    Jonathan M Beck

    • Edited by Beta Star Friday, September 7, 2012 4:38 PM
    Friday, September 7, 2012 3:44 PM
  • Another idea I had is that if I ensure that my Timesheets are created with: SvcTimeSheet.PreloadType.Default, then I am sure to have a LineGuid and an empty Actual.  Then to find the correct actual, call:

    SvcTimeSheet.TimesheetDataSet.ActualsRow actual =
                      timeSheetDs.Actuals.FindByTS_LINE_UIDTS_ACT_START_DATE(lineGuid, dates[i]);

    Then update the actual with:

    actual.TS_ACT_VALUE = newHours * 60 * 1000;

    Then execute this to save the time sheet in place:

    Guid jobGuid = Guid.NewGuid();
    SvcTimeSheet.TimesheetDataSet.HeadersRow header = timeSheetDs.Headers.Rows[0] as
    timeSheetSvc.QueueUpdateTimesheet(jobGuid, header.TS_UID, timeSheetDs);

    But this does not work either, it is giving me the "GeneralQueueCorrelationBlocked" error.

    Jonathan M Beck

    Friday, September 7, 2012 8:44 PM
  • I later found out that part of the problem with QueueUpdateTimeSheet() had to do with the service pack of the Project Server I was running.

    Installing the hotfix here helped address the issue:

    • Edited by SC_DOR_JB Wednesday, September 26, 2012 3:05 PM
    Wednesday, September 26, 2012 3:05 PM