- 9 Minutes to read
- Print
- DarkLight
- PDF
Concepts: System Objects with Custom Fields Intro
- 9 Minutes to read
- Print
- DarkLight
- PDF
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 aValue
object property that contains values and aReferenceObject
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, theValue
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 theLabel
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 BlueSet-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.