Concepts: Tasks & Task Forms
  • 24 Minutes to read
  • Dark
    Light
  • PDF

Concepts: Tasks & Task Forms

  • Dark
    Light
  • PDF

Article summary

Get a Collection of Tasks

To retrieve a collection of tasks, you use the Get-Tasks command. This command is like other collection commands where it can take Top, Skip, Filter, and OrderBy parameters.

# Retrieve tasks that are on hold and order them from newest to oldest
$tasks = Get-Tasks `
    -Filter "StatusDescription eq 'On Hold'" `
    -OrderBy "CreatedOn desc"
    
Write-Output "Located $($tasks.TotalCount) tasks that are on hold:"

# Do something with each task...
$tasks.Collection | ForEach-Object {
    # Retrieve the full task with forms (only if needed, otherwise this can slow down the script)...
    $task = Get-Task -Id $_.TaskId -IncludeForms
    
    # Do something with this task...
    Write-Output "Task $($task.TaskId) has $($task.Forms.Count) form(s)"
}

Output of this script:

Located 4 tasks that are on hold:
Task 1005 has 1 form(s)
Task 1004 has 2 form(s)
Task 1003 has 2 form(s)
Task 120 has 1 form(s)

Getting a Collection of Task Form Fields

If you’re interested in retrieving task form information based on specific fielded filters, then you should use the Get-TaskFormFields command. This command will retrieve a flat collection of task fields as individual records across all tasks allowing you to apply filters on the fields themself.

$taskFormFields = Get-TaskFormFields `
    -Filter "TaskFormName eq 'Blowbacks' and Label eq 'Number of Sets'"

# Add up all of the number fields...
$sumOfBlowbackSets = $taskFormFields.Collection | 
    Where-Object { $_.ValueAsNumber -ne $null } | 
    Measure-Object -Property ValueAsNumber -Sum

Write-Output "The total number of blowback sets is $($sumOfBlowbackSets.Sum.ToString('N0'))"

Output of this script:

The total number of blowback sets is 5,630

Get a Task Without Forms, Sections, and Fields

In some cases, you may want to retrieve a task that doesn’t contain any of it’s forms, sections, and fields because it is much faster to work with less bulky data by simply reading task-based system properties. To do this, use the Get-Task command to retrieve a single task which requires the id of a task.

$task = Get-Task -Id 120

$task | ConvertTo-Json -Depth 10

The output of this script:

{
  "Metadata": null,
  "AssignedBy": null,
  "AssignedTo": null,
  "Client": null,
  "CompletedBy": null,
  "CreatedBy": null,
  "Forms": [],
  "Matter": null,
  "Project": null,
  "QualityCheckPerformedBy": null,
  "Subscribers": [],
  "TaskId": 120,
  "ProjectId": 2016040000023,
  "ProjectTypeId": 1,
  "ProjectTypeName": "Normal",
  "ProjectTypeDescription": "The normal project type where task work is performed.",
  "ProjectSourceTypeId": 1,
  "ProjectSourceTypeName": "Not Specified",
  "ProjectDescription": "Process and Load Data",
  "ProjectComputedTags": "Relativity; Technical",
  "ProjectRequesterId": null,
  "ProjectRequesterFirstName": null,
  "ProjectRequesterLastName": null,
  "ProjectRequesterFullName": null,
  "ProjectRequesterEmail": null,
  "MatterId": 42,
  "MatterName": "Delta Bank SSB vs. Colorado Builders Construction CO.",
  "MatterReference": "2016-3422",
  "ClientId": 31,
  "ClientName": "Delta Bank SSB",
  "ClientReference": null,
  "Name": "Relativity Processing Request",
  "AssignedToId": "25a34a2f-e3d8-4690-88f5-6b399cf88c4c",
  "AssignedToFullName": "Max Miller",
  "AssignedById": "25a34a2f-e3d8-4690-88f5-6b399cf88c4c",
  "AssignedByFullName": "Max Miller",
  "EstimatedHours": null,
  "ActualHours": null,
  "StatusId": 4,
  "StatusDescription": "Completed",
  "QualityCheckStatusId": 1,
  "QualityCheckStatusDescription": "Not Performed",
  "QualityCheckPerformedById": null,
  "QualityCheckPerformedByFullName": null,
  "FormNames": "Relativity Processing Request",
  "SortOrder": 0,
  "DateDue": null,
  "CompletedOn": "2016-04-28T13:51:48.379262-05:00",
  "CompletedById": null,
  "CompletedByFullName": null,
  "AssignedOn": "2023-02-09T15:17:56.3981937+00:00",
  "NumberOfComments": 2,
  "NumberOfAttachments": 0,
  "NumberOfMediaLogEntries": 0,
  "NumberOfVolumes": 0,
  "NumberOfBillingEntries": 0,
  "NumberOfSubscribers": 0,
  "NumberOfEmailMessages": 0,
  "CreatedByFullName": "Max Miller",
  "CreatedById": "25a34a2f-e3d8-4690-88f5-6b399cf88c4c",
  "CreatedOn": "2016-04-28T13:43:45.5689454-05:00",
  "LastUpdatedByFullName": "Max Miller",
  "LastUpdatedById": "25a34a2f-e3d8-4690-88f5-6b399cf88c4c",
  "LastUpdatedOn": "2023-02-09T15:17:56.3981937+00:00",
  "LastUpdatedBy": null
}

Get a Task that Includes Forms, Sections, and Fields

In cases where you do need to retrieve a task’s forms, sections, and fields, you can add the -InlcudeForms switch to the Get-Task command. This command requires the id of the task.

$task = Get-Task -Id 120 -IncludeForms

$task | ConvertTo-Json -Depth 10

The output of this script:

{
  "Metadata": null,
  "AssignedBy": null,
  "AssignedTo": null,
  "Client": null,
  "CompletedBy": null,
  "CreatedBy": null,
  "Forms": [
    {
      "TaskFormId": 107,
      "TaskId": 120,
      "FormId": 0,
      "Guid": 1461868991662,
      "Name": "Relativity Processing Request",
      "Sections": [
        {
          "TaskFormSectionId": 118,
          "FormSectionId": null,
          "TaskFormId": 107,
          "Heading": "Processing Request",
          "Position": 0,
          "Guid": 1460996206057,
          "Fields": [
            {
              "TaskFormFieldId": 226,
              "FieldId": 0,
              "TaskFormSectionId": 118,
              "DataTypeId": 1,
              "DataTypeName": "Basic Text",
              "Label": "Processing Engine",
              "ValueAsString": "Relativity",
              "ValueAsBoolean": null,
              "ValueAsNumber": null,
              "ValueAsDecimal": null,
              "ValueAsDate": null,
              "DefaultValue": "Relativity",
              "IsRequired": true,
              "IsVisible": true,
              "Position": 0,
              "Guid": 1460996221915,
              "ReferenceObject": null,
              "AppliedProperties": []
            },
            {
              "TaskFormFieldId": 227,
              "FieldId": 0,
              "TaskFormSectionId": 118,
              "DataTypeId": 2,
              "DataTypeName": "Rich Text",
              "Label": "Processing Instructions",
              "ValueAsString": "<p>Process and load PST files collected from custodians per the following instructions:</p><ul><li>Process data to native format</li><li>Beginning Control Number: &nbsp;DBS00000001</li><li>Load Data into Relativity under folder Client Documents/Custodians</li></ul><p>More details</p>",
              "ValueAsBoolean": null,
              "ValueAsNumber": null,
              "ValueAsDecimal": null,
              "ValueAsDate": null,
              "DefaultValue": "<p>Process and load PST files collected from custodians per the following instructions:</p><ul><li>Process data to native format</li><li>Beginning Control Number: </li><li>Load Data into Relativity under folder Client Documents/Custodians</li></ul>",
              "IsRequired": true,
              "IsVisible": true,
              "Position": 1,
              "Guid": 1461176252114,
              "ReferenceObject": null,
              "AppliedProperties": []
            }
          ]
        }
      ]
    }
  ],
  "Matter": null,
  "Project": null,
  "QualityCheckPerformedBy": null,
  "Subscribers": [],
  "TaskId": 120,
  "ProjectId": 2016040000023,
  "ProjectTypeId": 1,
  "ProjectTypeName": "Normal",
  "ProjectTypeDescription": "The normal project type where task work is performed.",
  "ProjectSourceTypeId": 1,
  "ProjectSourceTypeName": "Not Specified",
  "ProjectDescription": "Process and Load Data",
  "ProjectComputedTags": "Relativity; Technical",
  "ProjectRequesterId": null,
  "ProjectRequesterFirstName": null,
  "ProjectRequesterLastName": null,
  "ProjectRequesterFullName": null,
  "ProjectRequesterEmail": null,
  "MatterId": 42,
  "MatterName": "Delta Bank SSB vs. Colorado Builders Construction CO.",
  "MatterReference": "2016-3422",
  "ClientId": 31,
  "ClientName": "Delta Bank SSB",
  "ClientReference": null,
  "Name": "Relativity Processing Request",
  "AssignedToId": "25a34a2f-e3d8-4690-88f5-6b399cf88c4c",
  "AssignedToFullName": "Max Miller",
  "AssignedById": "25a34a2f-e3d8-4690-88f5-6b399cf88c4c",
  "AssignedByFullName": "Max Miller",
  "EstimatedHours": null,
  "ActualHours": null,
  "StatusId": 4,
  "StatusDescription": "Completed",
  "QualityCheckStatusId": 1,
  "QualityCheckStatusDescription": "Not Performed",
  "QualityCheckPerformedById": null,
  "QualityCheckPerformedByFullName": null,
  "FormNames": "Relativity Processing Request",
  "SortOrder": 0,
  "DateDue": null,
  "CompletedOn": "2016-04-28T13:51:48.379262-05:00",
  "CompletedById": null,
  "CompletedByFullName": null,
  "AssignedOn": "2023-02-09T15:17:56.3981937+00:00",
  "NumberOfComments": 2,
  "NumberOfAttachments": 0,
  "NumberOfMediaLogEntries": 0,
  "NumberOfVolumes": 0,
  "NumberOfBillingEntries": 0,
  "NumberOfSubscribers": 0,
  "NumberOfEmailMessages": 0,
  "CreatedByFullName": "Max Miller",
  "CreatedById": "25a34a2f-e3d8-4690-88f5-6b399cf88c4c",
  "CreatedOn": "2016-04-28T13:43:45.5689454-05:00",
  "LastUpdatedByFullName": "Max Miller",
  "LastUpdatedById": "25a34a2f-e3d8-4690-88f5-6b399cf88c4c",
  "LastUpdatedOn": "2023-02-09T15:17:56.3981937+00:00",
  "LastUpdatedBy": null
}

Anatomy of Task Forms

All tasks contain a collection of forms that are referred to as task forms. These objects are created from workspace forms and maintain a snapshot as the form existed when the task was created. This allows users to modify or remove forms that were used on existing tasks without affecting those tasks.

Task forms mirror the structure of forms where each task form contains a collection of sections and each section contains a collection of fields. The crucial difference between forms and task forms is that task forms contain the values of the fields in the forms.

Below is an example of a task form that contains one section and two fields within that section.

{
   "TaskFormId": 107,
   "TaskId": 120,
   "FormId": 0,
   "Guid": 1461868991662,
   "Name": "Relativity Processing Request",
   "Sections": [
     {
       "TaskFormSectionId": 118,
       "FormSectionId": null,
       "TaskFormId": 107,
       "Heading": "Processing Request",
       "Position": 0,
       "Guid": 1460996206057,
       "Fields": [
         {
           "TaskFormFieldId": 226,
           "FieldId": 0,
           "TaskFormSectionId": 118,
           "DataTypeId": 1,
           "DataTypeName": "Basic Text",
           "Label": "Processing Engine",
           "ValueAsString": "Relativity",
           "ValueAsBoolean": null,
           "ValueAsNumber": null,
           "ValueAsDecimal": null,
           "ValueAsDate": null,
           "DefaultValue": "Relativity",
           "IsRequired": true,
           "IsVisible": true,
           "Position": 0,
           "Guid": 1460996221915,
           "ReferenceObject": null,
           "AppliedProperties": []
         },
         {
           "TaskFormFieldId": 227,
           "FieldId": 0,
           "TaskFormSectionId": 118,
           "DataTypeId": 2,
           "DataTypeName": "Rich Text",
           "Label": "Processing Instructions",
           "ValueAsString": "<p>Process and load PST files collected from custodians per the following instructions:</p><ul><li>Process data to native format</li><li>Beginning Control Number: &nbsp;DBS00000001</li><li>Load Data into Relativity under folder Client Documents/Custodians</li></ul><p>More details</p>",
           "ValueAsBoolean": null,
           "ValueAsNumber": null,
           "ValueAsDecimal": null,
           "ValueAsDate": null,
           "DefaultValue": "<p>Process and load PST files collected from custodians per the following instructions:</p><ul><li>Process data to native format</li><li>Beginning Control Number: </li><li>Load Data into Relativity under folder Client Documents/Custodians</li></ul>",
           "IsRequired": true,
           "IsVisible": true,
           "Position": 1,
           "Guid": 1461176252114,
           "ReferenceObject": null,
           "AppliedProperties": []
         }
       ]
     }
   ]
 }

In the example above, you may notice properties that start with “ValueAs”. These fields hold the value for the field based on the data type. When creating, reading, or updating task fields, it’s important to use the correct ValueAs* field. Fields can be defined with 10 different data types:

Data Type Id

Data Type Name

Stored As

1

Basic Text

ValueAsString

2

Rich Text*

ValueAsString

3

Whole Number

ValueAsNumber

4

Decimal Number

ValueAsDecimal

5

Yes or No Choice

ValueAsBoolean

6

Date Only

ValueAsDate

7

Date and Time

ValueAsDate

8

Single Choice

ValueAsString

9

Multiple Choice*

ValueAsString

10

Reference

ReferenceObject

Finding Fields

Tasks contain collections of forms, where each form contains collections of sections, and each section contains collections of fields. Below are a few methods you can use to help locate and pinpoint specific fields within task forms.

Iterating through each field

With this method, you use PowerShell’s ForEach-Object cmdlet to walk through each form, section, and field to locate the fields you’re interested in:

$task = Get-Task -Id 120 -IncludeForms

# Iterate through each form in the task
$task.Forms | ForEach-Object {
    $form = $_
    
    # Iterate through each section of the current form
    $form.Sections | ForEach-Object {
        $formSection = $_
        
        # Iterate through each field of the current section
        $formSection.Fields | ForEach-Object {
            $formField = $_
            
            if ($formField.Label -eq "Processing Instructions") {
                Write-Output "Encountered Form Field: $($formField.Label) (Data Type: $($formField.DataTypeName))"
                return
            }
        }
    }
}

This script produces the following example output for our example task:

Encountered Form Field: Processing Instructions (Data Type: Rich Text)

Piping and searching for a specific field by name

With this method, you still walk through forms and sections by piping the ForEach-Object results into each cmdlet, but you use PowerShell’s Where-Object cmdlet at the end of the pipeline to pluck out the specific field.

$task = Get-Task -Id 120 -IncludeForms

$processingInstructionsField = $task.Forms | 
    ForEach-Object { $_.Sections } | 
    ForEach-Object { $_.Fields } | 
    Where-Object { $_.Label -eq "Processing Instructions" }

Write-Output "Encountered Form Field: $($processingInstructionsField.Label) (Data Type: $($processingInstructionsField.DataTypeName))"

This script produces the following example output for our example task:

Encountered Form Field: Processing Instructions (Data Type: Rich Text)

Flattening the collection and searching for a specific field by name

With this method, you use PowerShell’s Select-Object cmdlet to flatten the fields into a single collection where you then use the Where-Object cmdlet to retrieve the desired field.

$task = Get-Task -Id 120 -IncludeForms

# Flatten the forms, sections, and fields into a single collection
$fields = $task.Forms |
    Select-Object -ExpandProperty Sections |
    Select-Object -ExpandProperty Fields

# Select the field based on the name of the field
$processingInstructionsField = $fields | Where-Object { $_.Label -eq "Processing Instructions" }

Write-Output "Encountered Form Field: $($processingInstructionsField.Label) (Data Type: $($processingInstructionsField.DataTypeName))"

This script produces the following example output for our example task:

Encountered Form Field: Processing Instructions (Data Type: Rich Text)

Fun Fact

In PowerShell, when you retrieve an object from a collection (like a field from a list of fields), you're working with a reference to the original object, not a separate copy. This means that any changes you make to that object will automatically be reflected in the original data structure from which it was retrieved.

Create a Task

When creating a task, you will need some information from a form that exists within the workspace. Below is an example of creating a task that uses a “Processing QC” form:

# Which project to create the task under
$projectId = 2024090000007

# Get the processing qc form
$qcForm = Get-Form -Id 62

# Get the current date
$currentDate = Get-Date

# Get Central Time Zone information to set a due date on the task in the correct time zone
$centralTimeZone = [TimeZoneInfo]::FindSystemTimeZoneById("Central Standard Time")

# Get the offset for the Central Time Zone
$offset = $centralTimeZone.GetUtcOffset($currentDate)

# Create a DateTimeOffset directly with the date components and Central Time offset, otherwise
# dates are saved in UTC, which is often not desiresd by end users. If you would like the
# task to be ongoing, you can skip the date stuff and just use $null for DateDue.
$dateDueWithOffset = [DateTimeOffset]::new(
    $currentDate.Year,
    $currentDate.Month,
    $currentDate.Day,
    17, # Hour
    0,  # Minute
    0,  # Second
    $offset
)

$qcTask = @{
  ProjectId = $projectId
  Name = "QC Processing Job"
  DateDue = $dateDueWithOffset
  Forms = @()
}

# To use a form for a task, we need to create a task form object that's
# based on the form we want to use. We'll loop through each section and
# field in the form and create a task form object that we can use to
# create the task.

# Forms contain sections, and sections contain fields.

$qcTaskForm = @{
  FormId   = $qcForm.FormId
  Sections = @()
}

$qcForm.Sections | ForEach-Object {
  $qcFormSection = $_

  $qcTaskFormSection = @{
    FormSectionId = $qcFormSection.SectionId
    Fields        = @()
  }

  $qcTaskForm.Sections += $qcTaskFormSection

  $qcFormSection.Fields | ForEach-Object {
    $qcFormSectionField = $_

    $qcTaskFormSectionField = @{
      FieldId = $qcFormSectionField.FieldId
    }

    # Check if there is a default value on the field. If there is, apply the default
    # to the appropriate ValueAs* field
    if (-not [string]::IsNullOrWhiteSpace($qcFormSectionField.DefaultValue)) {
      switch ($qcFormSectionField.DataTypeName) {
        "Whole Number" {
          # Convert the DefaultValue to an integer
          $qcTaskFormSectionField['ValueAsNumber'] = `
            [int]$qcFormSectionField.DefaultValue
        }
        "Decimal Number" {
          # Convert the DefaultValue to a decimal
          $qcTaskFormSectionField['ValueAsDecimal'] = `
            [decimal]$qcFormSectionField.DefaultValue
        }
        "Yes or No Choice" {
          # Convert the DefaultValue to a boolean
          $qcTaskFormSectionField['ValueAsBoolean'] = `
            [System.Convert]::ToBoolean($qcFormSectionField.DefaultValue)
        }
        { ($_ -eq "Date Only") -or ($_ -eq "Date and Time") } {
          # Convert the DefaultValue to a date
          $qcTaskFormSectionField['ValueAsDate'] = `
            [DateTimeOffset]$qcFormSectionField.DefaultValue
        }
        { ($_ -eq "Single Choice") -or ($_ -eq "Multiple Choice") } {
          # These fields don't have defaults, so they can be ignored unless you want
          # to add selected options during the task creation.
          break;
        }
        default {
          $qcTaskFormSectionField['ValueAsString'] = `
            $qcFormSectionField.DefaultValue
        }
      }
    }

    # Reference fields need to be handled in a different way. They exist as a
    # separate "ReferenceObject" property on the field and contain a list of
    # Values.
    if (($qcFormSectionField.DataTypeName -eq "Reference") `
        -and ($null -ne $qcFormSectionField.ReferenceObject -ne $null)) {

      # Create the reference object property that requires the object id and
      # the list of values.
      $qcTaskFormSectionField['ReferenceObject'] = @{
        ObjectId = $qcFormSectionField.ReferenceObject.ObjectId
        Values   = @()
      }

      # If the form has default reference values, we'll add them here
      $qcFormSectionField.ReferenceObject.DefaultValues | ForEach-Object {
        $refVal = @{
          KeyAsInteger = $_.KeyAsInteger
          KeyAsLong    = $_.KeyAsLong
        }

        # Again, PowerShell will convert $null string properties to an empty string,
        # so we'll handle objects that use a string as an ID separately here.
        if (-not [string]::IsNullOrWhiteSpace($_.KeyAsString)) {
          $refVal["KeyAsString"] = $_.KeyAsString
        }

        # Add the reference values to the reference object
        $qcTaskFormSectionField.ReferenceObject.Values += $refVal
      }
    }

    # Add the field to the section
    $qcTaskFormSection.Fields += $qcTaskFormSectionField
  }
}

# Finally, add the form to the task
$qcTask.Forms += $qcTaskForm

# Uncomment this line to view the task object that will be created before saving
# $qcTask | ConvertTo-Json -Depth 10

# Use the Agility Blue `Add-Task` PowerShell command followed by the 
# task object to create the task
$createdTask = Add-Task -Entry $qcTask

Write-Output "Created QC task $($createdTask.TaskId) in project $($createdTask.ProjectId)"

The output of this script:

Created QC task 1054 in project 2024090000007

Update a Task

After retrieving a task, you can update what you need to and save the same task back using the Set-Task command.

# This example will add a matter reference convention to the beginning of the task name if it doesn't already
# exist.

$task = Get-Task -Id 120

if ($task.Name.StartsWith("[$($task.MatterReference)]")) {
    Write-Output "Task $($task.TaskId) already begins with the matter reference in the name: $($task.Name)"
    exit
}

$task.Name = "[$($task.MatterReference)] $($task.Name)"
    
$updatedTask = Set-Task -Entry $task

Write-Output "Task $($task.TaskId) has been updated to begin with the matter reference in the name: $($task.Name)"

The output of this script:

Task 120 has been updated to begin with the matter reference in the name: [2016-3422] Relativity Processing Request

You can also update task form fields:

# Small helper function to retrieve fields from the task
function Get-Field ($task, $fieldLabel) {
    return $processingEngineField = $task.Forms | 
        ForEach-Object { $_.Sections } | 
        ForEach-Object { $_.Fields } | 
        Where-Object { $_.Label -eq $fieldLabel }
}

$task = Get-Task -Id 120 -IncludeForms

# Retrieve the "Processing Engine" field
$processingEngineField = Get-Field -task $task -fieldLabel "Processing Engine"

if ($null -eq $processingEngineField) {
    Write-Error "Unable to find a field named 'Processing Engine' on task $($task.TaskId)"
    exit
}

# Update the field. This particular field is a Basic Text field, so we want to set the
# ValueAsString property.
$processingEngineField.ValueAsString = "Nuix"

# Save the task. We don't need to update the forms or fields collection because any edits
# we do to fields we located are automatically updated within their collections.
Set-Task -Entry $task | Out-Null

# Confirm our updated field
$updatedTask = Get-Task $task.TaskId -IncludeForms
$updatedProcessingEngineField = Get-Field -task $updatedTask -fieldLabel "Processing Engine"

Write-Output "Updated the processing engine field on task $($updatedTask.TaskId) to `"$($updatedProcessingEngineField.ValueAsString)`""

The output of this script:

Updated the processing engine field on task 120 to "Nuix"

Assign a Task

To assign a task, you use the Set-AssignTask command which requires a task id and a user’s id for the user (or team) that you would like to assign the task to. If you don’t know a user’s id, but you know their email address or full name, you can use that information to filter the Get-WorkspaceUsers command.

$taskId = 120

# Retrieve a workspace user based on their full name. We're interested in the document
# reviewers team in this case
$users = Get-WorkspaceUsers `
    -Filter "FullName eq 'Document Reviewers' and IsTeamAccount eq true" `
    -Top 1

# Teams in Agility Blue are just a special type of user account
$documentReviewersTeam = $users.Collection | Select-Object -First 1

# Assign a task to a user/team using the UserId parameter
$updatedTask = Set-AssignTask `
    -TaskId $taskId `
    -UserId $documentReviewersTeam.Id
    
Write-Output "Assigned $($updatedTask.AssignedToFullName) to task $($updatedTask.TaskId)"

The output of this script:

Assigned Document Reviewers to task 120

Note

Assigning a task to someone will automatically set the task status to “In Progress” (StatusId 2)

Unassign a Task

To unassign a task, you use the Set-AssignTask command with a task id, but set the user id parameter to $null.

$taskId = 120

$updatedTask = Set-AssignTask `
    -TaskId $taskId `
    -UserId $null

if ($null -eq $updatedTask.AssignedToFullName) {
    Write-Output "Task $($updatedTask.TaskId) is no longer assigned"
    exit
}

# This shouldn't happen unless something went wrong
Write-Output "Task is still assigned tp $($updatedTask.AssignedToFullName) on task $($updatedTask.TaskId)"

The output of this script:

Task 120 is no longer assigned

Note

Unassigning a task will automatically set the task status to “New” (StatusId 1)

Change the Status of a Task

To change a task’s status, use the Set-TaskStatus command.

$taskId = 120

# Status Type ids are:
# --------------------------------
# New = 1
# In Progress = 2
# In Quality Control = 3
# Completed = 4
# On Hold = 5
# Canceled = 6
# --------------------------------

# An example of placing a task on hold would be:
$updatedTask = Set-TaskStatus `
    -TaskId $taskId `
    -StatusId 5

Write-Output "Task $($updatedTask.TaskId) status is now `"$($updatedTask.StatusDescription)`""

The output of this script:

Task 120 status is now "On Hold"

Note

Unlike within the UI, you can set the task status to any of the status types without requiring comments or assignments. For example, a task that is unassigned can be set to “In Progress” or tasks can be set to “On Hold” or “Canceled” without requiring a comment. These behaviors that require that information are purely UI driven and not a part of the core system.

Change the Quality Check Status of a Task

To set the quality check status of a task, use the Set-TaskQualityCheckStatus command. This command requires a task id, a quality check status type id, and an optional status id.

$taskId = 120

# Quality Check Status Type ids are:
# --------------------------------
# Not Performed: 1
# Passed: 2
# Rejected: 3

# Providing the task status id is optional, but is a convenient way to complete a task at the same time
# as updating the qc status. An example of changing the qc status to passed and also completing the task is:
$updatedTask = Set-TaskQualityCheckStatus `
    -TaskId $taskId `
    -QualityCheckStatusTypeId 2 `
    -StatusId 4

# This shouldn't happen unless something went wrong
Write-Output "Task $($updatedTask.TaskId) QC status is `"$($updatedTask.QualityCheckStatusDescription)`" and the task status is set to `"$($updatedTask.StatusDescription)`""

The output of this script:

Task 120 QC status is "Passed" and the task status is set to "Completed"

Reopen a Previously Closed Task

To reopen a task that was previously closed, use the Set-ReopenTask command that requires only a task id.

$taskId = 120

$updatedTask = Set-ReopenTask -TaskId $taskId

Write-Output "Task $($updatedTask.TaskId) has been reopened and the status is now `"$($updatedTask.StatusDescription)`""

The output of this script:

Task 120 has been reopened and the status is now "In Progress"

Note

The same effect can be accomplished by using the Set-TaskStatus command by changing the status type to 1 or 2 depending on if the task was assigned or not.

Delete a Task

Deleting tasks can be achieved by using the Remove-Task command that requires a task id. Here is an example that shows how to delete a task:

$taskId = 120

Write-Output "Deleting task $taskId..."

Remove-Task -Id $taskId

# Try to retrieve the task to prove that it was deleted
$task = Get-Task -Id $taskId

if ($null -eq $task) {
    Write-Output "A task with an id of $taskId doesn't exist"
    exit
}

# The next line should not happen
Write-Error "Uh-oh, task id $taskId still exists after attempting to delete it"

The output of this script:

Deleting task 120...
A task with an id of 120 doesn't exist

Points of Interest:

  • In the script above, we deleted the task first and then tried to retrieve it to prove that it was deleted. This isn’t a bad idea if you want to ensure something was deleted, but in optimized situations, you would only need to delete the entry because if something went wrong during the deletion process, the server would throw an exception that would be logged in the output.

Be Careful!

Deleting data cannot be reversed, so make sure you thoroughly test your code to minimize mistakes!

Tasks Attached to Event Triggers

If you are interested in attaching an update event trigger to a task, there are additional considerations about the event that need to be addressed, particularly for Before Save actions.

All update events also contain event actions that help describe the type of event that occurred. These can be thought of like “sub events”, and they are stored in a special variable named $ABVAREventAction. For most objects, this variable will be populated directly with the main event, such as CREATE, UPDATE, and DELETE. For tasks, though, there are a variety of update actions that a user can perform on a task that populate this event action variable with different information depending on the type of update they perform. This needs to be used within before-save event scripts to help identify the type of action they performed and is also important because the Get-InboundObjectInstance command will provide different data depending on these actions which dictates the return type needed. Below is a table of actions along with the type of data that the Get-InboundObjectInstance would provide.

Write-Output $ABVAREventAction 

$ABVAREventAction (Event Action/Sub-Event)

Model

Notes

CREATE

AgilityBlue.ViewModels.TaskInternalModel

Occurs when a user creates a task. The task is provided when using Get-InboundObjectInstance.

UPDATE

AgilityBlue.ViewModels.TaskInternalModel

Occurs when a user edits a task. The task is provided when using Get-InboundObjectInstance.

DELETE

AgilityBlue.ViewModels.TaskInternalModel

Occurs when a user deletes a task. The task is provided when using Get-InboundObjectInstance.

ASSIGN

AgilityBlue.ViewModels.KeyContainer

Occurs when a user assigns a task. The user id string is provided when using Get-InboundObjectInstance.

ADD_FORMS

List<AgilityBlue.ViewModels.Workspace.TaskFormViewModel>

Occurs when a user adds forms to a task. A list of task forms is provided when using Get-InboundObjectInstance.

QUALITY_CONTROL

Dictionary<string, int>

Occurs when a user updates the quality control status of a task. A hash table (dictionary) of the quality control status id and task status id is provided when using Get-InboundObjectInstance.

DONE

AgilityBlue.ViewModels.KeyContainer

Occurs when a user completes a task. The task id is provided when using Get-InboundObjectInstance.

Scripts that rely on the update action of a task should take this information into consideration for update events, otherwise the script may fail because it does not emit the correct return result for the action.

For example, if you are interested in creating a script that only responds when the user completes a task, you need to filter out the other update actions by returning them back to the system and focus in on the “DONE” event action.

# Retrieve the incoming instance
$inboundObj = Get-InboundObjectInstance

# This script only cares about the update sub-event where it indicates that a task was marked commpleted
if ($ABVAREventAction -ne "DONE") {
    Write-Output "Ignoring task sub-event $ABVAREventAction"

    if ($ABVAREventAction -eq "ADD_FORMS") {
        # Adding Forms event special handling:
        # Add a comma to force powershell into creating an array because it will convert single-item arrays to an object.
        return ,$inboundObj
    }

    return $inboundObj
}

# Becausee the type of event is a "DONE" event, it only provides the task id as a "Key"
# Retrieve the task ID from the inbound object
$taskId = Get-InboundObjectId
$task = Get-Task $taskId -IncludeForms

# Continue on processing with the rest of the script...

Points of interest:

  • Before-Save scripts require the same return type as the input type, so you need to check the $ABVAREventAction variable and ensure you returning the correct type.

  • The ADD_FORMS event action needs to be returned as a list/array. PowerShell will auto-convert single-element lists into a single object, so use a comma before the variable to force the variable to be returned as a list.

  • For After-Save scripts, you could ignore the $ABVAREventAction variable by just using Get-Task that uses the Get-InboundObjectId, since that will always return the task id, and can help simplify the script.