Concepts: System Objects with Custom Fields Intro
  • 9 Minutes to read
  • Dark
    Light
  • PDF

Concepts: System Objects with Custom Fields Intro

  • Dark
    Light
  • PDF

Article summary

Introduction

This article explores the concepts behind system objects and custom fields. There are a number of system objects that allow the use of custom fields:

  • Clients

  • Matters

  • Media Log Entries

  • Volumes

  • Billing Entries

  • Contacts

When a system object allows for custom fields, the paradigm for creating, reading, and updating them are the same. The object will contain their system fields on the root of the object and maintain information about custom fields and their values in a collection property named Fields. Each field object within the fields collection will contain metadata about the field, such as the Label, DataTypeName, and a Value object containing the actual values. Depending on the data type, values are stored in properties that start with ValueAs* where the asterisk here indicates the type (string, date, number, etc.). If the data type is a reference field, there is an object property named ReferenceObject that stores the reference values for the field.

Here’s an example of a client object instance where there are 3 custom fields defined on the client object:

PowerShell Script to retrieve the subsquent result:
Get-Client -Id 3 | ConvertTo-Json -Depth 10

{
  "ClientId": 3,
  "Name": "Alpha Widgets",
  "Reference": "1032",
  "Active": false,
  ... Some read-only system fields are removed for readability ...
  "Fields": [
    {
      "FieldId": 10,
      "ObjectId": 1,
      "DataTypeId": 1,
      "DataTypeName": "Basic Text",
      "Label": "Billing Contact Name",
      "IsRequired": true,
      "Position": 0,
      "Guid": 1475776072753,
      "IsSystemField": false,
      "IsReferenceValue": false,
      "DefaultValue": null,
      "CopyPreviousValueOnSaveAndNew": false,
      "Value": {
        "ObjectFieldValueId": 92,
        "ObjectFieldId": 10,
        "ObjectId": 1,
        "PrimaryKeyId": 3,
        "ValueAsString": "Joseph Mauer",
        "ValueAsBoolean": null,
        "ValueAsNumber": null,
        "ValueAsDecimal": null,
        "ValueAsDate": null,
        "ReferenceObject": null
      },
      "ReferenceObject": null
    },
    {
      "FieldId": 14,
      "ObjectId": 1,
      "DataTypeId": 2,
      "DataTypeName": "Rich Text",
      "Label": "Client Address",
      "IsRequired": false,
      "Position": 2,
      "Guid": 1476994440044,
      "IsSystemField": false,
      "IsReferenceValue": false,
      "DefaultValue": null,
      "CopyPreviousValueOnSaveAndNew": false,
      "Value": {
        "ObjectFieldValueId": 95,
        "ObjectFieldId": 14,
        "ObjectId": 1,
        "PrimaryKeyId": 3,
        "ValueAsString": "<p>1600 Puckett Drive N</p><p>Minneapolis, MN 55402</p>",
        "ValueAsBoolean": null,
        "ValueAsNumber": null,
        "ValueAsDecimal": null,
        "ValueAsDate": null,
        "ReferenceObject": null
      },
      "ReferenceObject": null
    },
    {
      "FieldId": 21,
      "ObjectId": 1,
      "DataTypeId": 1,
      "DataTypeName": "Basic Text",
      "Label": "Billing Contact Email",
      "IsRequired": true,
      "Position": 1,
      "Guid": 1500308948435,
      "IsSystemField": false,
      "IsReferenceValue": false,
      "DefaultValue": null,
      "CopyPreviousValueOnSaveAndNew": false,
      "Value": {
        "ObjectFieldValueId": 94,
        "ObjectFieldId": 21,
        "ObjectId": 1,
        "PrimaryKeyId": 3,
        "ValueAsString": "jmauer@alphawidgets.com",
        "ValueAsBoolean": null,
        "ValueAsNumber": null,
        "ValueAsDecimal": null,
        "ValueAsDate": null,
        "ReferenceObject": null
      },
      "ReferenceObject": null
    }
  ]
}

Points of Interest:

  • You will notice that each custom field within the Fields property have the same structure where there is first metadata about a field, then there is a Value object property that contains values and a ReferenceObject if the data type is “Reference”.

  • The metadata information about a field can help you determine the different data types, determining which ValueAs* property to store the value in. Here are the 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

    *Note that rich text fields save data with HTML markup so the browser can format the value properly. The HTML that is accepted is limited for security reasons and Agility Blue will ignore executing <script> tags and rendering anything in the inline style attributes.

    ** Note that multiple choice values can be separated by the newline character per selected choice: \n

In our example above, the important properties to consider here is the Label property and the ValueAsString property, because all 3 of our fields deal with custom fields that store their values as strings.

Create an Object Instance With Custom Fields

When creating an object instance that allows for custom fields, you will need to provide system fields for that object that are required and all of the custom fields that are required unless you include the BypassCustomFieldValidation switch when invoking the add command. If you don’t have any custom fields, or if you want to bypass custom field validation, you can ignore the Fields collection entirely. The following example shows how we can create a client that ignores creating the custom fields and bypasses any of the required custom fields:

$clientToCreate = @{
    Name        = "QuantumTech v. CyberInnovate"
    Reference   = "QT00001"
}

$createdClient = Add-Client -Entry $clientToCreate -BypassCustomFieldValidation

Write-Output "Client $($createdClient.ClientId) ($($createdClient.Name)) was created"

The output of this script:

Client 79 (QuantumTech v. CyberInnovate) was created

To create a client with custom fields, you will need to provide the Fields collection parameter along with each custom field the object expects. In our running example here, we have 3 custom fields defined on the client object that we’ll provide:

$billingContactNameFieldId  = 10
$billingContactEmailFieldId = 21
$clientAddressFieldId       = 14

$clientToCreate = @{
  Name      = "SoundWave Studios II"
  Reference = "SWS0001"
  Fields    = @(
        @{
            FieldId = $billingContactNameFieldId
            Label   = "Billing Contact Name"
            Value   = @{
            ValueAsString = "Seth Trapsitell"
          }
        },
        @{
            FieldId = $billingContactEmailFieldId
            Label   = "Billing Contact Email"
            Value   = @{
            ValueAsString = "Seth.Trapsitell@agilitybluedemo.com"
          }
        },
        @{
            FieldId = $clientAddressFieldId
            Label   = "Client Address"
            Value   = @{
            ValueAsString = $null
          }
        }
    )
}

$createdClient = Add-Client -Entry $clientToCreate

Write-Output "Client $($createdClient.ClientId) ($($createdClient.Name)) was created"

The output of this script:

Client 83 (SoundWave Studios) was created

Points of Interest:

  • Note that we did not have to provide every property on a field object. We only needed to provide the relevant metadata properties and the relevant ValueAs* property.

  • The FieldId property is required. You can find this ID within the application by navigating to the objects page and edit the object like you would if you were to add new fields. You will find the CF_# field ID on the top left of any custom field in this view.

  • In our example above, we included the Label property for readability. That field is Read-Only and is ignored by Agility Blue if provided, but having it there quickly helps us see what fields we’re addressing within the collection.

Retrieve a Custom Field

In PowerShell, you can retrieve a specific custom field by the FieldId property or by the Label property by using the Where-Object command. Here is an example that uses the Label property to retrieve the billing contact name field. Remember, PowerShell will treat objects like this as reference objects, meaning any properties you update on this new object will be reflected on the object within the original collection. This is useful in places where you may only want to update a specific field, so you can pull it out like this by reference rather than having to iterate over each field in the collection.

$client = Get-Client -Id 3
$billingContactNameField = $client.Fields | Where-Object { $_.Label -eq "Billing Contact Name" }

Write-Output "The billing contact name for $($client.Name) is $($billingContactNameField.Value.ValueAsString)"

The output of this script:

The billing contact name for Alpha Widgets is Joseph Mauer

Heads Up!

You should add null checks in your scripts when accessing dot-notation properties that may be null. For example, in the previous script, the Value property was directly accessed without first checking to see if it was null. Generally speaking, the Value property should be available, but it’s good practice to check, especially if custom fields are added after records already exist. The previous script could be written like the following for null safety:

$client = Get-Client -Id 3
$billingContactNameField = $client.Fields | Where-Object { $_.Label -eq "Billing Contact Name" }

# A field can be null if the field was added after the billing entry was created
if ($null -eq $billingContactNameField) {
    $billingContactNameField = @{}
}

if ($null -eq $billingContactNameField.Value) {
    $billingContactNameField.Value = @{}
}

Write-Output "The billing contact name for $($client.Name) is $($billingContactNameField.Value.ValueAsString)"

Update an Object Instance With Custom Fields

The flow for updating objects is typically like this: Retrieve object, update properties on that object, either save the object back to Agility Blue in the case of manual script execution, or return the modified object back to Agility Blue in the case of an event trigger. Using the knowledge that we just learned in the previous section on retrieving custom fields, we’ll update a billing contact name with a new billing contact name and save it back to the system:

$clientToUpdate = Get-Client -Id 3
$billingContactNameField = $clientToUpdate.Fields | Where-Object { $_.Label -eq "Billing Contact Name" }

# A field can be null if the field was added after the client was created
if ($null -eq $billingContactNameField) {
    $billingContactNameField = @{}
}

if ($null -eq $billingContactNameField.Value) {
    $billingContactNameField.Value = @{}
}

# Update the value to something different. Updating this object will also update it on the Client.Fields collection.
$billingContactNameField.Value.ValueAsString = "Eugene M. Randle"

# Save the client back to the system
$updatedClient = Set-Client -Entry $clientToUpdate

# Repeat the original steps above to pull out the updated fields and output what's there now
$updatedBillingContactNameField = $updatedClient.Fields | Where-Object { $_.Label -eq "Billing Contact Name" }

Write-Output "The billing contact name for $($updatedClient.Name) is $($updatedBillingContactNameField.Value.ValueAsString)"

The output of this script:

The billing contact name for Alpha Widgets is Eugene M. Randle

Points of Interest:

  • We first retrieved the client that we wanted to update and then pulled out the billing contact name custom field using PowerShell’s Where-Object cmdlet based on the Label property.

  • We added some null safety checks to make sure we weren’t trying to update a value object that doesn’t exist. If it turns out that the value object was null, it simply created a new empty object using the @{} notation.

  • The ValueAsString property was updated with a new value because we knew that this field is a basic text field. By updating this field on this object, it also reflected the changes in the original $clientToUpdate.Fields collection due to how PowerShell object referencing works.

  • Since we know that the $clientToUpdate object was updated in memory, it was time to update it within the system by using the Agility Blue Set-Client cmdlet. This command will return the updated client back to us that we can use to ensure our changes were saved correctly.

  • The rest of the script simply checks the updated client and outputs the updated billing contact name.