Use Server-Side Java API to Create/Modify Timesheet Entries (ITimeSheetEntry)
I have found several examples on these forums of how to programmatically create a Timesheet entry using the Java API. However, all of the examples that I've seen (including the Wiki) are for client-side java. I need to get this working for server-side Java, but have been unsuccessful.
It seems like I am almost there. When the code executes, it seems to create a phantom link to a timesheet that doesn't actually exist. After I visit a work item where this follow-up action was triggered, I get a java.lang.NullPointerException from the eclipse interface complaining that the target item doesn't exist. So it seems like the timesheet entry that I create, is destroyed or lost when the follow-up action terminates.
Here is the code:
public class Create_Timesheet_Entry_From_Comment extends AbstractService implements IOperationParticipant
{
boolean Debug = true;
@Override
public void run(AdvisableOperation operation, IProcessConfigurationElement participantConfig, IParticipantInfoCollector collector, IProgressMonitor monitor) throws TeamRepositoryException
{
Object data = operation.getOperationData();
SaveParameter saveParam = (SaveParameter)data;
//To avoid recursion, immediately exit if this participant was triggered by a previous execution of this participant
if(saveParam.getAdditionalSaveParameters().contains("CreateTimesheetEntryFromCommentOperationParticipantTriggeredThisSave"))
return;
IAuditable auditable = saveParam.getNewState();
if(!(auditable instanceof IWorkItem))
return;
if(Debug) System.out.println("The Create_Timesheet_Entry_From_Comment extension was triggered.");
IWorkItem thisWI = (IWorkItem)auditable;
IWorkItemServer service = getService(IWorkItemServer.class);
IWorkItem UpdateWorkItem = (IWorkItem) service.getAuditableCommon().resolveAuditable(thisWI, IWorkItem.FULL_PROFILE, null).getWorkingCopy();
//Examine the existing timesheet entries
IWorkItemReferences references = service.resolveWorkItemReferences(UpdateWorkItem, null);
ArrayList<IReference> ExistingTimeSheetEntries = new ArrayList<IReference>();
if(!references.getReferences(WorkItemEndPoints.WORK_TIME).isEmpty())
ExistingTimeSheetEntries.addAll(references.getReferences(WorkItemEndPoints.WORK_TIME));
IAuditableCommon iac = saveParam.getSaveOperationParameter().getAuditableCommon();
for(IReference thisEntry : ExistingTimeSheetEntries)
{
ITimeSheetEntry thisEntryItem = iac.resolveAuditable((ITimeSheetEntryHandle)thisEntry.resolve(), ITimeSheetEntry.FULL_PROFILE, null);
PrintTimesheetEntry(thisEntryItem);
if(Debug) return;
}
ITimeSheetEntry entry = service.createTimeSheetEntry(UpdateWorkItem.getProjectArea());
List<ITimeCode> timeCodes = service.getTimeCodes(UpdateWorkItem.getProjectArea());
ITimeCode timeCode = timeCodes.get(0);
Identifier<ILiteral> workType = Identifier.create(ILiteral.class, UpdateWorkItem.getWorkItemType());
Timestamp now = new Timestamp(System.currentTimeMillis());
entry.setStartDate(now);
entry.setTimeSpent(new Duration(2 * 60 * 60 * 1000));
entry.setTimeCodeId(timeCode.getTimeCodeId());
entry.setWorkType(workType);
entry.setTimeCode(timeCode.getTimeCodeLabel());
entry.setCreator(getAuthenticatedContributor());
entry.setRequestedStateId(UUID.generate());
if(Debug) {
System.out.println("The timesheet entry's settings are:");
PrintTimesheetEntry(entry);
}
/* //This method has never worked for any link type creation. Using the ILinkService has worked, but only for WorkItems
IWorkItemReferences workItemReferences = saveParam.getNewReferences();
if(Debug) System.out.println("Adding the newly created timesheet to the work item's references.");
workItemReferences.add(WorkItemEndPoints.WORK_TIME, IReferenceFactory.INSTANCE.createReferenceToItem(entry.getItemHandle()));
UpdateWorkItem.setRequestedModified(now);
Set<String> additionalParams = new HashSet<String>();
additionalParams.add("CreateTimesheetEntryFromCommentOperationParticipantTriggeredThisSave");
service.saveWorkItem3(UpdateWorkItem, null, null, additionalParams);*/
if(Debug) System.out.println("Attempting to create a link to the timesheet.");
LinkItemsWithoutPreconditions((IItem)UpdateWorkItem, (IItem)entry, WorkItemLinkTypes.TIME_SHEET_ENTRY);
LinkItemsWithoutPreconditions((IItem)entry, (IItem)UpdateWorkItem, WorkItemLinkTypes.TIME_SHEET_ENTRY_TARGET);
}
//NOTE: This method was given as an alternative, but I was cautioned against using it because it doesn't trigger operation behavior
//In my case, I think that's what I want.
void LinkItemsWithoutPreconditions(IItem sourceItem, IItem targetItem, String linkType) throws TeamRepositoryException
{
IReferenceFactory refFactory = IReferenceFactory.INSTANCE;
IReference source = refFactory.createReferenceToItem(sourceItem.getItemHandle());
IReference target = refFactory.createReferenceToItem(targetItem.getItemHandle());
if(source == null && Debug) System.out.println("The source is null.");
if(target == null && Debug) System.out.println("The target is null.");
if(source == null || target == null) return;
ILinkService linkService = getService(ILinkService.class);
ILinkServiceLibrary linkServiceLibrary = (ILinkServiceLibrary) linkService.getServiceLibrary(ILinkServiceLibrary.class);
ILink link = linkServiceLibrary.createLink(linkType, source, target);
linkServiceLibrary.saveLink(link);
}
public void PrintTimesheetEntry(ITimeSheetEntry entry)
{
System.out.println("\tStart Date = " + entry.getStartDate());
System.out.println("\tTimeCodeId = " + entry.getTimeCodeId());
System.out.println("\tHoursSpent = " + entry.getHoursSpent());
System.out.println("\tWorkType = " + entry.getWorkType());
System.out.println("\tTimeCode = " + entry.getTimeCode());
System.out.println("\tTimeSpent = " + entry.getTimeSpent());
System.out.println("\tCreator = " + entry.getCreator());
}}
There's a lot of code there, but I include it all for context. Here's the relevant part I think:
LinkItemsWithoutPreconditions((IItem)UpdateWorkItem, (IItem)entry, WorkItemLinkTypes.TIME_SHEET_ENTRY);
LinkItemsWithoutPreconditions((IItem)entry, (IItem)UpdateWorkItem, WorkItemLinkTypes.TIME_SHEET_ENTRY_TARGET);
This method has been successful for me in the past in linking work items. However, most of the examples that I've seen seem to suggest I should be using an approach like this:
IWorkItemReferences workItemReferences = saveParam.getNewReferences();
workItemReferences.add(WorkItemEndPoints.WORK_TIME, IReferenceFactory.INSTANCE.createReferenceToItem(entry.getItemHandle()));
service.saveWorkItem3(UpdateWorkItem, null, null, additionalParams);
I haven't used this second approach because I've never been able to get it to work for saving links between work items. I've tried it here for Timesheet entries anyway, but it didn't seem to work here either.
The examples that I've seen seem to emphasize the importance of a command like:
workingCopy.getDependentItems().add(entry);
However, that command doesn't seem to be available in the same way in a server-side extension.
I'd appreciate any help anyone can offer.
One answer
In the code above, if you create a reference, you have to add the reference to saveWorItem3 as a parameter to get them saved.
IStatus saveStatus = fWorkItemServer.saveWorkItem3(workItem, references, workFlowAction, additionalParams);