As I get further and further into the Project Server Interface (PSI), I run up against gaps in my knowledge and some times the community's knowledge, as it exists in searchable form online.  This little "gotcha's" can sometimes mean hours of research and experimentation.  I thought I would start a new series entitled "It's the Little Things" to capture some of this knowledge.  It would be great if others would chip in so send me your little tips and tricks and I will catalogue them here.

So, for the first of the series, let me tell you a story about timesheets.

I am working on a project to populate current timesheets with timesheet lines from the previous timesheet.  The population is done in the timesheet OnCreated event.  

Let's take a look a the code:

   1: foreach (TimesheetDataSet.LinesRow line in previousTimesheet.Lines)
   2: {
   3:     Guid lineClassGuid;
   4:  
   5:     if (IsCorrectForCopy(line, classifications, out lineClassGuid))
   6:     {
   7:         TimesheetDataSet.LinesRow newLine = currentTimesheet.Lines.NewLinesRow();
   8:  
   9:         newLine.TS_UID = e.TsUID;
  10:         newLine.TS_LINE_UID = Guid.NewGuid();
  11:         newLine.TS_LINE_CLASS_UID = lineClassGuid;
  12:         newLine.TS_LINE_COMMENT = string.Empty;
  13:         newLine.TS_LINE_STATUS = (byte)TimesheetEnum.LineStatus.NotApplicable;
  14:         newLine.TS_LINE_VALIDATION_TYPE = (byte)TimesheetEnum.ValidationType.Unverified;
  15:         newLine.TS_LINE_CACHED_PROJ_NAME = Settings.Default.projectName;
  16:         newLine.TS_LINE_CACHED_ASSIGN_NAME = Settings.Default.taskName;
  17:         newLine.TS_LINE_STATUS = (byte)TimesheetEnum.Status.InProgress;
  18:  
  19:         currentTimesheet.Lines.AddLinesRow(newLine);
  20:  
  21:         projectServer.Timesheets.PrepareTimesheetLine(newLine.TS_UID,
  22:                                                       ref currentTimesheet,
  23:                                                       new[] { newLine.TS_LINE_UID });
  24:         
  25:     }
  26: }
  27:  
  28: Guid jobGuid = Guid.NewGuid();
  29:  
  30: projectServer.Timesheets.QueueUpdateTimesheet(jobGuid,
  31:                                               e.TsUID,
  32:                                               currentTimesheet);
  33:  
  34: string errorMessage;
  35:  
  36: projectServer.Queue.WaitOnJobStatus(jobGuid,
  37:                                     JobState.Success,
  38:                                     projectServer.Settings.QueueStatusRetryCount,
  39:                                     projectServer.Settings.QueueStatusSleepDuration,
  40:                                     out errorMessage);
  41:  
  42: if (!string.IsNullOrEmpty(errorMessage))
  43: {
  44:     EventLog.WriteEntry(Settings.Default.AppName, errorMessage, EventLogEntryType.Warning);
  45: }

When I first started working on this, I didn't set the ASSN_UID field because I don't have an assignment GUID--it isn't a timesheet line for an assignment.  PrepareTimesheetLine was bombing.  The key is to properly set TS_LINE_VALIDATION_TYPE = TimesheetEnum.ValidationType.Unverified.

This indicates to the API to ignore the missing ASSN_UID in calls to both PrepareTimesheetLine and QueueUpdateTimesheet.

Because I had been staring at the timesheet user interface in PWA, where the word "unverified" showed up on timesheet lines that weren't associated with tasks, I assumed setting the validation type was simply cueing the rendering engine to display "unverified" versus what it was really doing: telling the validation engine to consider the timesheet line unverified thus ignoring the lack of a GUID on the ASSN_UID field. Duh!

Its the little things...