Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

You can control project’s permission schema to control who can log work on an issue or use workflow status properties to control on which statuses work can be logged on an issue but sometimes you may need more complex controls. Following scripts are just examples of such conditions all of which are required by some of our customers.

Don't Allow Logging Work

...

If Remaining Estimate is Zero or Not Specified

Following scripts do not allow any worklog to be higher than if remaining estimate of an issue . If there is no remaining estimate, users can log any durationis zero or not specified. This script works on 'Before Worklog Create/Update' script type.

language
Code Block
groovy
def issue = worklog.getIssue();
ifdef (remainingEstimate=issue.getEstimate();
== nullif (remainingEstimate== null || remainingEstimate == 0 ) { //no remaining estimate on issue, don't allow logging work
  return; }
if (issue.getEstimate() < worklog.getTimeSpent()) {
  return "Maximum duration you can log is " + issue.getEstimate() / 60 + " minute(s)""Can't log work because the remaining estimate is '0' or 'Not Specified'";
}

Don't

...

This script does not allow logging work on issues which has subtasks. This is applied in use cases where a parent task is divided to subtasks and each subtask is separately estimated.

Code Block
if (worklog.getIssue().getSubTaskObjects().size() > 0) {
  return "If an issue has subtasks, you can only log work on subtasks of the issue"
}

Don't allow logging work on subtasks

Simply the reverse of above script.

Code Block
if (worklog.getIssue().isSubTask()) {
  return "You can't log work on subtasks"
}

Don't allow logging work on specific issue types

Following script does not allow logging work if issue type is “Task”.

...

Allow Logging Work If Original Estimate is Zero or Not Specified

This script prevents the worklog entry to an issue belonging to the ATP project in case the value of the original estimate field is null or zero.

Code Block
import com.deniz.jira.worklog.*;
def issue = worklog.getIssue();
def project = issue.getProject().key;
def originalEstimate = issue.getOriginalEstimate();
def originalEstimateCheck = !originalEstimate || originalEstimate == 0;
if(project == "ATP" && originalEstimateCheck){
  return "Can not log work!";
}

Don't Allow Logging Work Higher Than Remaining Estimate

Following scripts do not allow any worklog to be higher than remaining estimate of an issue. If there is no remaining estimate, users can log any duration.

Code Block
languagegroovy
def issue = worklog.getIssue();
if (issue.getEstimate() == null) { //no estimate on issue, allow logging work
  return;
}
if (issue.getEstimate() < worklog.getTimeSpent()) {
  return "YouMaximum duration you can't log workis " for+ issue type 'Task'";.getEstimate() / 60 + " minute(s)"
}

Don't allow logging work on

...

issues

...

which has subtasks

This script does not allow logging work on issues which has subtasks. This is applied in use cases where a parent task is divided to subtasks and each subtask is separately estimated.

Code Block
if (worklog.getIssue().getSubTaskObjects().getResolutionsize() !=> null0) {
  return "YouIf can'tan logissue work for resolved issues";has subtasks, you can only log work on subtasks of the issue"
}

Don't allow logging work

...

on subtasks

Simply the reverse of above script.

Code Block
if (worklog.getIssue().getStatusisSubTask().getName() == 'Done') {
  return "You can't log work for issues in 'Done' issues status";on subtasks"
}

Don't allow logging work on specific issue types

Following script does not allow logging work

...

if issue type is “Task”.

Code Block
if (worklog.getTimeSpent.getIssue().getIssueType().getName() < 1800== "Task") {
// 60 (seconds per minute) * 30 (minutes) = 1800 seconds
  return "Time spent return "You can't log bework for lessissue thantype 30m'Task'";
}

...

Don't allow logging work on resolved issues

Code Block
if ((worklog.getTimeSpentgetIssue() % 900.getResolution() != 0null) {
// 60 (secondsreturn per minute) * 15 (minutes) = 900 seconds
  return "Please log 15 minutes or more, and if more, ensure it is in blocks of 15 minutes, e.g. 30m, 1.5h, etc";
}

Billable Time can't Be Larger than Time Spent

Code Block
def billableTime = worklogAttributes.get(4); //update id with actual id of "Billable Time Worklog Attribute" for your system
if (Integer.valueOf(billableTime) > worklog.getTimeSpent()) {"You can't log work for resolved issues";
}

Don't allow logging work when an issue is in a specific status

Code Block
if (worklog.getIssue().getStatus().getName() == 'Done') {
  return "You can't log work for issues in 'Done' issues status";
}

Don't allow logging work less than 30m

Code Block
if (worklog.getTimeSpent() < 1800) { // 60 (seconds per minute) * 30 (minutes) = 1800 seconds
  return "BillableTime timespent can't be largerless than time Spent30m";
}

...

Logged Time should be in block of 15 minutes

Code Block
importif com.atlassian.jira.security.groups.GroupManager;
import com.atlassian.jira.component.ComponentAccessor;
 
def projects = ["ERP", "ACN"]; //projects users are allowed to work
def issue = worklogResult.getWorklog().getIssue();
def project = issue.getProjectObject();
def GroupManager groupManager = ComponentAccessor.getGroupManager();
if (!projects.contains(project.getKey())) {
    String author = worklog.getAuthorObject();
    Collection<String> groupNamesForUser = groupManager.getGroupNamesForUser(author);
    if(groupNamesForUser.contains("restricted-users")) { //users in this group can only work on selected projects
      return "You can't work on this project. You can only work on:" + projects;
    }
}((worklog.getTimeSpent() % 900) != 0) { // 60 (seconds per minute) * 15 (minutes) = 900 seconds
  return "Please log 15 minutes or more, and if more, ensure it is in blocks of 15 minutes, e.g. 30m, 1.5h, etc";
}

Billable Time can't Be Larger than Time Spent

Code Block
def billableTime = worklogAttributes.get(4); //update id with actual id of "Billable Time Worklog Attribute" for your system
if (Integer.valueOf(billableTime) > worklog.getTimeSpent()) {
  return "Billable time can't be larger than time Spent";
}

If Users are in specific user group only allows logging work on subset of projects

Code Block
import com.atlassian.jira.security.groups.GroupManager;
import com.atlassian.jira.component.ComponentAccessor;
 
def projects = ["ERP", "ACN"]; //projects users are allowed to work
def issue = worklogResult.getWorklog().getIssue();
def project = issue.getProjectObject();
def GroupManager groupManager = ComponentAccessor.getGroupManager();
if (!projects.contains(project.getKey())) {
    String author = worklog.getAuthorObject();
    Collection<String> groupNamesForUser = groupManager.getGroupNamesForUser(author);
    if(groupNamesForUser.contains("restricted-users")) { //users in this group can only work on selected projects
      return "You can't work on this project. You can only work on:" + projects;
    }
}

Don't Allow Specifying a Work Start Date After Due Date of the Issue

...

If Users are in specific user group only allows logging work on subset of projects

Code Block
import com.atlassian.jira.security.groups.GroupManager;
import com.atlassian.jira.component.ComponentAccessor;
 
def projects = ["ERP", "ACN"]; //projects users are allowed to work
def issue = worklogResult.getWorklog().getIssue();
def project = issue.getProjectObject();
def GroupManager groupManager = ComponentAccessor.getGroupManager();
if (!projects.contains(project.getKey())) {
    String author = worklog.getAuthorObject();
    Collection<String> groupNamesForUser = groupManager.getGroupNamesForUser(author);
    if(groupNamesForUser.contains("restricted-users")) { //users in this group can only work on selected projects
      return "You can't work on this project. You can only work on:" + projects;
    }
}

Don't Allow Specifying a Work Start Date After Due Date of the Issue

Code Block
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.security.JiraAuthenticationContext;
import java.text.SimpleDateFormat;
import java.time.Duration;
 
def authenticationContext = ComponentAccessor.getJiraAuthenticationContext();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd", authenticationContext.getLocale());
 
def issue = worklog.getIssue();
def dueDate = issue.getDueDate(); //2020-04-22 00:00:00, due dates default to 00:00:00
if (dueDate != null) {
  def dueDateNight = Date.from(dueDate.toInstant().plus(Duration.ofHours(23)).plus(Duration.ofMinutes(59))); //20-04-22 23:59:00
   
  //the date on which work is performed, not the day worklog is created
  def workStartDate = worklog.getStartDate();
   
  //If you need to consider timezone differences of users you may want to use LocalDateTime
  if (workStartDate.after(dueDateNight)) {
    return "You can't specify a work start date after " + simpleDateFormat.format(dueDate);
  } 
}

Don't Allow Worklog After a Specific Date Specified on Issue

Code Block
import com.atlassian.jira.security.groups.GroupManager
import com.atlassian.jira.component.ComponentAccessor

def cfm = ComponentAccessor.getCustomFieldManager()
def issue = worklog.getIssue()
def endDateField = cfm.getCustomFieldObject('customfield_11300') //custom field id of date field we are interested
def endDate = endDateField.getValue(issue) as Date
if(endDate){
  if (worklog.getStartDate() > endDate + 1){
    return "You can't specifyonly alog worktime startbefore dateor after " + simpleDateFormatduring ${endDate.format(dueDate);'dd.MM.yyyy')}"
  }

}

Don't Allow

...

a User to Log Overlapping Worklogs

Code Block
import com.atlassian.jira.issue.worklog.*;
import java.time.*;
import java.time.temporal.*;
import com.deniz.jira.securityworklog.groupsservices.GroupManager*;
import com.atlassian.jira.component.ComponentAccessor
 
def cfm = ComponentAccessor.getCustomFieldManager()
def issue = worklog.getIssue()
def endDateField = cfm.getCustomFieldObject('customfield_11300') //custom field id of date field we are interested
def endDate = endDateField.getValue(issue) as Date
 
if (worklog.getStartDate() > endDate + 1){
    return "You can only log time before or during ${endDate.format('dd.MM.yyyy')}"
}

Don't Allow a User to Log Overlapping Worklogs

Code Block
import com.atlassian.jira.issue.worklog.*;
import java.time.*;
import java.time.temporal.*;
import com.deniz.jira.worklog.services.*;
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.security.JiraAuthenticationContext;
 
def timesheetService = ComponentAccessor.getOSGiComponentInstanceOfType(TimesheetService.classComponentAccessor;
import com.atlassian.jira.security.JiraAuthenticationContext;
 
def timesheetService = ComponentAccessor.getOSGiComponentInstanceOfType(TimesheetService.class);
 
def issue = worklog.getIssue();
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser();
def newWorklogStart = ZonedDateTime.ofInstant(worklog.getStartDate().toInstant(), ZoneId.systemDefault());
ZonedDateTime startTime = newWorklogStart.truncatedTo(ChronoUnit.DAYS);
ZonedDateTime endTime = newWorklogStart.plusSeconds(worklog.timeSpent);
 
def newWorklogStartInMilis = newWorklogStart.toInstant().toEpochMilli();
def newWorklogEndInMilis = newWorklogStart.toInstant().plusSeconds(worklog.timeSpent).toEpochMilli();
 
def worklogWrapper = timesheetService.getUsersWorklogs(startTime, endTime, true, null, null, null, null, currentUser.getUsername());
 
def issueerrorMessage = worklog.getIssue();
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser();
def newWorklogStart = ZonedDateTime.ofInstant(worklog.getStartDate().toInstant(), ZoneId.systemDefault());
ZonedDateTime startTime = newWorklogStart.truncatedTo(ChronoUnit.DAYS);
ZonedDateTime endTime = newWorklogStart.plusSeconds(worklog.timeSpent);
 
def newWorklogStartInMilis = newWorklogStart.toInstant().toEpochMilli();
def newWorklogEndInMilis = newWorklogStart.toInstant().plusSeconds(worklog.timeSpent).toEpochMilli();
 
def worklogWrapper = timesheetService.getUsersWorklogs(startTime, endTime, true, null, null, null, null, currentUser.getUsername());
 
def errorMessage = null;
worklogWrapper.projects.each { project ->
  project.issues.each { wIssue ->
    wIssue.workLogs.each { pWorklog ->
      if (pWorklog.id != worklog.id) {null;
worklogWrapper.projects.each { project ->
  project.issues.each { wIssue ->
    wIssue.workLogs.each { pWorklog ->
      if (pWorklog.id != worklog.id) {
          def pWorkStartInMilis = pWorklog.workStart.toInstant().toEpochMilli();
          def pWorkEndInMilis = pWorklog.workStart.toInstant().plusSeconds(pWorklog.getTimeSpent()).toEpochMilli();
          if ((newWorklogStartInMilis >= pWorkStartInMilis && newWorklogStartInMilis <= pWorkEndInMilis) ||
              (newWorklogStartInMilis <= pWorkEndInMilis def&& pWorkStartInMilisnewWorklogEndInMilis >= pWorklog.workStart.toInstant().toEpochMilli();
 pWorkStartInMilis)) {
        def pWorkEndInMilis = pWorklog.workStart.toInstant().plusSeconds(pWorklog.getTimeSpent()).toEpochMilli();  errorMessage = "Collides with worklog which started on " if+ ((newWorklogStartInMilis >= pWorkStartInMilis && newWorklogStartInMilis <= pWorkEndInMilis) ||pWorklog.workStart.toString() + ". Please check your timesheet.";
          }
    (newWorklogStartInMilis <= pWorkEndInMilis}
&& newWorklogEndInMilis >= pWorkStartInMilis)) {}
  }
}
 
return errorMessage;

Don’t Allow Non Service Desk Users to Create Worklogs

Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.security.JiraAuthenticationContext;
 errorMessage = "Collides with worklog which started on " + pWorklog.workStart.toString() + ". Please check your timesheet.";
          }
      }
    }
  }
}
 
return errorMessage;

Don’t Allow Non Service Desk Users to Create Worklogs

Code Block
languagegroovy
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.security.JiraAuthenticationContext;
import com.atlassian.jira.security.roles.ProjectRoleManager;
import com.deniz.jira.worklog.services.attr.AttrTypeService;

def authenticationContext = ComponentAccessor.getJiraAuthenticationContext();
def projectRoleManager = ComponentAccessor.getComponent(ProjectRoleManager.class);

//We need to load WorklogPRO classes differently using getOSGiComponentInstanceOfType
def attrTypeService = ComponentAccessor.getOSGiComponentInstanceOfType(AttrTypeService.class);

def billableAttrType = attrTypeService.getAttrTypeWithName("Billable").get();
if (billableAttrType == null) {
  return; //there is no billable attribute
}

def isBillable = worklogAttributes.get(billableAttrType.getID());
def loggedInUser = authenticationContext.getLoggedInUser();
def serviceDeskTeamRole = projectRoleManager.getProjectRole("Service Desk Team");
def project = worklog.getIssue().getProjectObject();

println(worklogAttributes);

if (isBillable == "yes") { //boolean attributes are has either "no" or "yes" value.
  if (!projectRoleManager.isUserInProjectRole(loggedInUser, serviceDeskTeamRole, project)) import com.atlassian.jira.security.roles.ProjectRoleManager;
import com.deniz.jira.worklog.services.attr.AttrTypeService;

def authenticationContext = ComponentAccessor.getJiraAuthenticationContext();
def projectRoleManager = ComponentAccessor.getComponent(ProjectRoleManager.class);

//We need to load WorklogPRO classes differently using getOSGiComponentInstanceOfType
def attrTypeService = ComponentAccessor.getOSGiComponentInstanceOfType(AttrTypeService.class);

def billableAttrType = attrTypeService.getAttrTypeWithName("Billable").get();
if (billableAttrType == null) {
  return; //there is no billable attribute
}

def isBillable = worklogAttributes.get(billableAttrType.getID());
def loggedInUser = authenticationContext.getLoggedInUser();
def serviceDeskTeamRole = projectRoleManager.getProjectRole("Service Desk Team");
def project = worklog.getIssue().getProjectObject();

println(worklogAttributes);

if (isBillable == "yes") { //boolean attributes are has either "no" or "yes" value.
  if (!projectRoleManager.isUserInProjectRole(loggedInUser, serviceDeskTeamRole, project)) {
    return "Only Service Desk Team can register billable worklogs!"
  }
}

Don't Allow Worklog Without Comment Except for Defined Projects

Code Block
import com.deniz.jira.worklog.*;
def worklogComment = worklog.comment;
def issue = worklog.getIssue();
def workloggedProject = issue.getProject().key.toString();
def exemptProjects= ["ATP", "SP"];

if(!exemptProjects.contains(workloggedProject) && !worklogComment){
    return "OnlyComment Service Desk Team can register billable worklogs!"
  }is required.";
}