Calculated Fields in ShareDo

You can use calculated fields to expand the data model using configured or captured data. For instance, you might want to fill calculated fields based on data captured in other fields.

Calculated fields allow you to write short scripts to extract any ShareDo data. Using calculated fields is ideal for document generation or template administration. As well as extracting the data, you can perform basic operations on it, like comparing data, returning a value based on the comparison, and then formatting it. For example, you can calculate a participant's age and return this in words rather than numbers. You can also reference other calculated fields from within a field.

Term Definition
Calculated field In ShareDo, you can use expressions and scripts to reference, build, or retrieve data to use as a value.
Expression An expression is a function used to get a piece of data, like a claimant's last name.
Script A script is a series of expressions forming a more complex data retrieval and formatting function. For example, getting task completed dates from a matter and presenting them as a list in a document formatted with ordinals and the month name spelt out: the 3rd of March.

Creating a Calculated Field

You can create calculated fields in any data composer explorer. For a work type's data composer explorer, go to Modeller > Work Types > {Your WorkType} > Data Composer. For a complete list of where to find the Data Composer, see the article Data Composer.

When creating your calculated field, consider at which level you want to add it to your data model. Remember your work types are hierarchical. If you have a parent work type of 'matter' and want your field to apply to all its child types, you should add the calculated field at the matter level in the work type modeller. If you want it to be specialised for a specific work type - add it directly to the work type.

Example

Click on the + button at the top of the required area in Data Composer to add your field.

For example, if you want to add a calculated field to a work type, select the work type, select Data Composer > Work Item, then click the + button as shown in this image.

The calculated field blade opens.

 

Important note on the context of functions

The functions listed here include the workitem context, for example: workitem.roles.client.ods.person.lastName. Typically, you would set the context when creating the calculated field and if the context is set to Work Item (as in the image above), the workitem part of your references is not needed.

 
Enter the name, display name, and description for your calculated field, then enter your expression (see below for help on operators, keywords, and examples of expressions).
Use the buttons to save your progress.

The buttons at the top right of the editor have the following functions:

Opens the debugger (see below).
Opens an expression help blade. The blade shows syntax, common functions, examples, and a version changelog.
Validates the expression to ensure the syntax is correct. Errors will be presented at the foot of the window:                                   
Toggles the expression window between full-screen view and normal view.

Operators & Keywords

The following is a list of the operators and keywords you can use when creating calculated fields.

Note that you must have spaces on either side of an operator in your code, and strings must be enclosed in single quotes, e.g. 'hello world'.

Also, note that square brackets […] can only be used to denote arrays, e.g. [313081,31002,31003]. Therefore [title] is resolved as an array containing the title string.

 
Operator / Keyword Description
+ Add numeric values or concatenate strings e.g. firstname + ' ' + lastname
- Subtract numeric values.
* Multiply numeric values.
/ Divide numeric values.
= Equals.
<> Not equal to, e.g. workitem.roles.client.ods.person.lastName <> 'Fred'
> Greater than.
>= Greater than or equal to.
< Less than.
<= Less than or equal to.

in

Matches one of the values listed.
and And.
or Or.
true True.
false False.
null Null.

Examples 

Operator

Example

workitem.subtype.id = 31003

Testing whether the sub-work type has an ID of 31003.  

Returns True if a match is made. 

In 

workitem.subtype.id in [313081,31002,31003]

Testing whether the sub-work type is any one of the three values. 

Returns True if a match is made. 

count(workitem.roles.buyer) > 1

There is more than one Buyer on the property transaction. 

Returns True if a match is made. 

workitem.roles.buyer.firstname + ' ' + workitem.roles.buyer.lastname

Returns "Joe Bloggs" 

Add the two fields together, with a space in between. 

Using Fields with Special Characters

When using fields that have query strings containing special characters, you must ‘escape’ those characters for the query to be valid. For example, the following query string will raise an error:      
financialTransactions!q?code=CLIENT;REC;DEBTPRINCIPLE&transactionTypes=invoice&total=true.amount

The error would be similar to: Syntax error (line 1, column 36): unexpected `;`.

You must ‘escape’ the semicolons with a backslash:

financialTransactions!q?code=CLIENT\;REC\;DEBTPRINCIPLE&transactionTypes=invoice&total=true.amount

Common Functions

The following is a list of commonly used functions that you can use to build up your expressions within calculated fields.

In the table below, the function parameter my_field refers to the field against which you run the function. A comma separates any further parameters, and function parameters are enclosed in brackets.

You can find a list of Culture Codes here: Supported Culture Codes

How to find a particular function in this reference

You can either use the Ctrl+f find function of your browser, or find and click the function in this list.

Functions, descriptions, and examples
toUpper(my_field)
 

Changes the case to uppercase. 

Example

toUpper(workitem.roles.client.ods.person.lastName) 

Converts Smith to SMITH. 

 

toLower(my_field)
 

Changes the case to lowercase.

Example

toLower(workitem.roles.client.ods.person.lastName)

Converts Smith to smith.

 

toTitleCase(my_field) 
 

Changes to title case (first letter upper case).

Example

toTitleCase(workitem.roles.client.ods.person.lastName)

Converts smith to Smith.

 

Note: this function only works for text in lowercase. It will not convert all uppercase text to title case. To do this, combine the two functions together as follows: ToTitleCase(tolower(my_field))

 

isNullOrEmpty(my_field) 
 

Returns true if the field's value is null or an empty string.

Example

IsNullOrEmpty(workitem.keyDates.kd-file-review.taskDueDate.date.utc.value.toString())

Returns true if the work item's File Review Key Date has been set, else false.

Important: This function only applies to Strings. To use this function with a number, like a date, you must convert it to a string with toString().       
E.g., to see if a date is older than 7 years:      
if (isNullOrEmpty(keyDates.kd-initial-instructions-confirmed.taskDueDate.date.utc.value.toString()))       
Then false       
Else dateAdd('y', 7 , keyDates.kd-initial-instructions-confirmed.taskDueDate.date.utc.value) < now()

 

 

startsWith(my_field, 'string to check') 
 

Returns true if the string starts with the characters provided. 

Example

startsWith(workitem.type.systemName, 'matter-dispute')

Returns true if the work item system name starts with matter-dispute.      
 

startsWithIgnoreCase(my_field, 'string to check')
 

Returns true if the string starts with the characters provided, regardless of case. 

 

endsWith(my_field, 'string to check')
 

Returns true if the string ends with the characters provided.

Example

endsWith(workitem.type.systemName, 'dispute')

Returns true if the work item system name ends with dispute.      
 

endsWithIgnoreCase(my_field, 'string to check')
 

Returns true if the string ends with the characters provided, regardless of case.

 

contains(my_field, 'string to check')
 

Returns true if the field contains the string specified. 

Example

contains(workitem.type.path,'defendant')

Returns true if the work item system name

matter-dispute-defendant-pi-medical-negligence

contains the word defendant

 

containsIgnoreCase(my_field, 'string to check')
 

Returns true if the field contains the string specified, regardless of case. 

 

truncate / left (my_field,number of characters)

right (my_field,number of characters)

 

Returns all the text to the left (or right) of a string, for the number of characters specified. 

Example

left(workitem.roles.client.ods.person.firstName,1)            
truncate(workitem.roles.client.ods.person.firstName,1)

Converts John to J

toUpper(workitem.roles.client.ods.person.lastname) + ' ' +           
toUpper(truncate(workitem.roles.client.ods.person.firstName, 1))

Converts John Smith to SMITH J.

This kind of function can be used to build up names in the format required by specific documents. It is frequently used in ODS calculated fields to create dedicated court or contract participant names.

 

count(my_list)
 

Counts the number of items in a list. 

Example

count(workitem.roles.witness)

Returns the number of witness participants assigned to the work item.  

E.g., returns: "3" 

count(children!q?path=proceeding&closed=true)

Counts the number of proceedings created (whether currently open or closed) on a matter.

 

If Then Else
 

If statement that will check a condition, and then return specified values for true and false

Example

if (count(workitem.roles.witness)) > 0           
Then           
'Has witnesses'           
Else           
'Does not have witnesses'

Checks if there are one or more witnesses, if so, it returns the text Has witnesses. If there are none, it returns Does not have witnesses.

 

If subtype.id = 13081 Then true Else false

If the id of work item subtype is 313081, then return true, else return false.

Note: this can simply be abbreviated to:

subtype.id = 313081 

as the default return values are true or false.

 

Nested Ifs can be used so that each condition is tested in turn, until a positive result is found.

For example:     
The following code checks the Matter Jurisdiction and returns the appropriate office address. If no Jurisdiction is found, the default Perth address is returned:

If(matterJurisdiction.jurisdiction.name) = 'Victoria'     
Then '150 Bourke Street, Melbourne VIC 3000'     
Else     
If(matterJurisdiction.jurisdiction.name) = 'New South Wales'     
Then '12 Castlereagh Street, Sydney NSW 2000'     
Else     
If(matterJurisdiction.jurisdiction.name) = 'Queensland'     
Then '64 Eagle Street, Brisbane QLD = 4000'     
Else     
'19 St Georges Terrace, Perth WA 6000'

 

now
 

Returns today's date.

Example

$root.env.now.utc.value

Returns: 

2023-09-25T13:43:33.7516307Z 

The date is returned in the default format of the browser. 

See the format expression, below on how to apply formats to dates.      
 

An example of checking if a date is greater than now:      
parseDate(keyDates.kd-initial-instructions-confirmed.taskDueDate.date.utc.value,'en-GB') > now()

 

dateDiff(startDate,endDate,datePart)
 

Return the difference between two dates, as per the datePart, namely: 'ms' (milliseconds), 's' (seconds), 'm' (minutes), 'd' (days), 'y' (years). 

Example

dateDiff(workitem.roles.claimant!1.ods.person.dateOfBirth, $root.env.now.utc.value,'y')

Returns the age of the 1st claimant in years, by comparing the number of years between their birth date and today's date.

 

dateDiff(workitem.children!q?path=kd-instruction-received&closed=true!1.taskDueDate.date.utc.value, workitem.keyDates.kd-re-exchange-date.taskDueDate.date,'d') 

Calculates the number of days from the date of the initial instruction to a property exchange date. 

 

dateAdd(datePart,number,date)
 

Adds a number to a date, as per the datePart value, namely: 'ms' (milliseconds), 's' (seconds), 'm' (minutes), 'h' (hours), 'd' (days), 'M' (months), 'y' (years). 

Commonly used with the format function to format the results. 

Example

dateAdd('d', 10, workitem.keyDates.kd-incident-date.taskDueDate.date.utc.value)

Adds 10 days to the date that instructions were received. 

Returns:

2023-08-29T09:36:43.2100000+00:00 

dateAdd('y', -1 , workitem.keyDates.kd-incident-date.taskDueDate.date.utc.value)

Subtracts 1 year from the date of an incident. 

Returns:

2023-08-01T08:00:00.0000000+00:00 

 

parsedate(my_field, 'culturecode') 
 

Converts the string representation of a date and time to a 'DateTime' equivalent, using the mandatory culture code.

Commonly used to convert date fields held on Form Builder forms (as they are actually stored in ShareDo in string format).

Example

parseDate(workitem.form-sample-date.dateField1,'en-GB')

Converts a Form Builder date from a string to a date, using the British culture code.

Returns: 

2023-09-12T00:00:00.0000000+00:00

The same field, but using the American culture code:

parseDate(form-sample-date.dateField1,'en-US')

Returns:

2023-12-09T00:00:00.0000000+00:00

Note the days/months are reversed.

The format function can be used in conjunction with parseDate:  

format(parseDate(form-sample-date.dateField1,'en-GB'),'dd MMM yyyy')

Returns:

12 Sep 2023       
 

format(myfield,format)
 

Formats the value using the specified format (and optional culture code if specified). 

Typically used with dates and number values. 

Examples

Date example: 

format($root.env.now.utc.value, 'd MMMM yyyy')

Presents todays date in the format requested. 

Note: the ordinal can be applied to the day component: 

format($root.env.now.utc.value, 'ddos MMMM yyyy')

Result: 28th September 2023 

Just return the year component:

format($root.env.now.utc.value, 'yyyy')

Result: 2023 

Format can be combined with other functions: 

format(dateAdd('d', -182, keyDates.kd-incident-date.taskDueDate.date.utc.value), 'd MMMM yyyy')

This subtracts 6 months from the incident date and formats the result: 

1 September 2023 

.. and now including the ordinal: 

format(dateAdd('d', -182, keyDates.kd-incident-date.taskDueDate.date.utc.value), 'dddos MMMM yyyy')

Result: 1st September 2023 

 

Number examples: 

Using the culture code of "DE" (Germany) to present currency in the format used in Austria and Germany (e.g. 500,00.00) 

workitem…..damages.value!q?format=@n&cultureCode=de-DE      
 

Using a precision e.g. 'n', 'n2' to control decimal places.            
'n' - number.

format(12345,'n2') returns 12,345.00      
 

'p' - percentage.

format(0.39755, 'p2')  

Returns 39.75%      

'c' - currency.      
format (12345, 'c2')      
Returns £12,345.00

 

'c' - currency field with country code.     
format(name of field.value, 'c2', 'countrycode')     
For example:     
format(form-custom-legal-qualification.legalQualificationPreIncidentSalary.value, 'c2', 'en-AU')     
Returns $3,434.78     
This can be combined with the use of variables.     
For example:     
var total1 =     
form-custom-mb-legal-qualification.legalQualificationPreIncidentSalary.value     
+     
form-custom-mb-legal-qualification.legalQualificationPostIncidentSalary.value     
;     
format(total1, 'c2', 'en-AU')

 

numberToWords(my_field,case)
 

Returns a number expressed in words. 

Case - 'lower' (default), 'upper', 'title' 

Example

numberToWords (document.questionnaire.attempts_made_to_contact.value, 'title')

Returns: "Two".

numberToWords(workitem.no_of_witness)

Where no_of_witness is a Calculated Field

containing a Count of all Witnesses. 

Returns: "Three" 

Note that as the default is "lower", this does not need to be passed. 

numberToWords(dateDiff(workitem.roles.occupier!1.ods.person.dateOfBirth,$root.env.now.utc.value,'d'), 'upper')

Returns: "SEVENTEEN" 

Calculates the age of the first occupier participant living in property for sale. 

 

currencyAmountToWords(my_field, currencyCode,case)
 

Returns a currency expressed in words. 

currencyCode - supported values are: 'usd', 'cad', 'aud', 'gbp' 

Case - 'lower' (default), 'upper', 'title' 

Example

currencyAmountToWords(workitem.descendants!q?type=offer-received-settlement&closed=true!1.offerTransactions.totalAmount, 'gbp', 'upper')

Converts the amount of the most recent offer received (£15,500.01) into British Pounds and returns the value as text, in capital letters:

"FIFTEEN THOUSAND FIVE HUNDRED POUNDS AND ONE PENCE" 

currencyAmountToWords(document.questionnaire.pay_amount.value, 'usd', 'title')

Converts the amount input into a Form Builder questionnaire on a prepare document blade into US Dollars, using title case. 

"Twelve Dollars and Fifty-Five Cents" 

 

replace(my_field, oldValue, newValue)
 

Returns a new string in which all occurrences of a value are replaced with a new value. 

Example

replace(roles.claimant!1.ods.contact.direct-line!1.value,'+44 ','0')

This example replaces the international country code from the claimants telephone number with 0. 

 

N.B. This can also be used on the ODS, making it available to all entities/participants.

For example:     
On the root of ODS.contact, create a calculated field with the following:     
replace(direct-line!q?primary=true.value,'+44 ','0')     
This will now be available for the primary Direct Dial number on all ODS entries.

 

round(my_field)
 

Rounds a decimal value to the nearest whole number. 

Example

For example, a dateDiff function returns the total number of days between two key dates, as follows:

e.g. 13.59069479188 days 

Using the round function: 

round(dateDiff(roles.claimant!1.ods.person.dateOfBirth, $root.env.now.utc.value,'d'))

The result would be 14 days.

 

floor(my_field)
 

Rounds a value down to the nearest whole number. 

Example

A dateDiff function may return 13.59069479188 days.

Using the floor function: 

floor(dateDiff(roles.claimant!1.ods.person.dateOfBirth, $root.env.now.utc.value,'d'))

Would return 13 days.

 

ceil(my_field)
 

Rounds a value up to the nearest whole number. 

Example

A dateDiff function may return 13.59069479188 days. 

Using the ciel function: 

ciel(dateDiff(roles.claimant!1.ods.person.dateOfBirth, $root.env.now.utc.value,'d'))

Would return 14 days.

 

coalesce(my_field1,my_field2,my_field3)
 

Evaluates the arguments in order and returns the value of the first expression that doesn't evaluate to null.

Example

coalesce(roles.landlord!1.ods.name, roles.land-owner!1.ods.name, roles.freeholder!1.ods.name)

Where one or more participant roles could be assigned to a work item, then cycle through each of the participant names (in order specified) and return the first name found. 

 

substring(my_field,startIndex,length)
 

Returns a substring that starts at the specified character position (startIndex) and continues to the end of the string 

for the length of characters specified. The index is 0 based.

Example

substring(workitem.reference, 2, 2)

This extracts all the text from the 2nd character to the 4th character. 

e.g. D.MD.23.0012 becomes MD.

 

To return a substring where you do not know the start index or the length of the value you need to retrieve, for example if you need everything after the . in the following:     
QWERTY.6666232

you can use this as an example:

var startIndex = indexOf(workitem.reference, '.') + 1;     
substring(workitem.reference, startIndex, workitem.reference.length() - startIndex)

This would return:

6666232

 

length(string)
 

Returns the number of characters in a string.

Example

length(workitem.reference)

If the workitem reference is D.MD.23.0012, the length function returns 12.

 

indexOf(string,pattern,comparisonType)
 

Returns the zero-based index of the first occurrence of the specified substring in the string. The comparisonType is an optional boolean (true or false). false (default) for case-sensitive comparison, true for case insensitive.

Example

indexOf(workitem.reference, 'M')

Where the workitem reference is D.MD.23.0012

Returns 2.

 

lastIndexOf(string,pattern,comparisonType)
 

Returns the zero-based index of the last occurrence of the specified substring in the string.  The comparisonType is an optional boolean (true or false). false (default) for case-sensitive comparison, true for case insensitive.

Example

lastIndexOf(workitem.reference, 'D')

Where the workitem reference is D.MD.23.0012

Returns 3.

 

split
 

Splits a string based on a specified delimiter into an array of sub-strings.

Example

‘ABC.12345’.split('.')

Returns ['ABC', ‘12345’]

 

You can use the elementAt(index) function (0 based) to access either part:

'ABC.12345'.split('.').elementAt(0)

Returns 'ABC'

 

elementAt(list, index)
 

Looks up the specified index number in a list. For example, find the 3rd item in the list.

Example

elemantAt(workitem.roles.witness, 2)

Returns the 2nd witness in the list.

 

pluck
 

Returns a new list constructed from the specified property of each element in the list. 

Example

A participant may have more than one role on a work item (e.g. both client and claimant). 

Where we need to extract the participant role of a recipient to a document, the results are therefore returned as a list.

Using the pluck expression allows us to extract what we need from that list. 

For example, on a document template that has a To: Recipient role, create a document calculated field on Document Recipient > Context Participant. Use the expression:

pluck(roles, 'roleType.systemName')

This would return the system names of the role of the selected document recipient: e.g. "client,claimant" 

This can be combined with the If Then Else statement. 

e.g. Where a letter is directed to one of a number of participant roles, you may wish to amend the subject line of the letter to best reflect the recipient's interest: 

if(contains(pluck(roles, 'roleType.systemName'), 'client'))           
Then           
'Your claim'           
else           
'Your clients claim'

In this example, when writing to either the client or any of their representatives: if the recipient is the client, return the words "Your claim", else return "Your client's claim". 

 

var
 

Declare a local variable and assign a value to it.      
Use the ; character to end the declaration.      
Example

var sub_total1 =       
fee-estimate-drafting-amount.value +       
fee-estimate-due-diligence-amount.value      
;      
var sub_total2 =       
fee-estimate-deduction-of-title-amount.value +       
fee-estimate-financial-charges-release-amount.value      
;      
Var total = sub_total1 + sub_total2

;      
Format(total, ‘c2’)

 

This creates two local variables to capture two sub totals.      
It then creates a third variable and adds the two sub totals together to store the total value.

Finally, it formats the total as a Currency value.  

 

NB! If any field values in the above calculation are Null, the sub totals / total may also return Null.      
Use the coalesce command to replace any Null field values with 0      
For example: 

coalesce(fee-estimate-deduction-of-title-amount.value, 0)      
 

Mathematical Calculations 

() x2 / x + - 

 

Calculations can be performed using the standard BODMAS rule.

Example

currencyAmountToWords(document.questionnaire.weekly_pay_amount.value * 52, 'usd' , 'title') =

Takes the amount input by the user into a prepare document questionnaire field and multiplies it by 52. 

It is then formatted to return the value in words, in US format: 

One Thousand Three Hundred and Forty-Four Dollars 

Finally, note that hitting Ctrl-Space on the keyboard will present a list of all available functions:

                        
The examples above reference the most commonly used functions.

Advanced Calculated Fields

ShareDo uses Lambda functions – single lines of code similar to SQL functions, including select and where/filter, which are used to help write expressions. As the functions may return multiple values, the results are transformed into a single value, which can be formatted as required. Be aware that filter is an alias of where, they perform the same function.

E.g. selecting all the occupiers of a sale property where the occupier is over 18 and transforming this list into a single comma-separated string of participant names that can be used in emails and documents. Or returning a comma-separated list of all payment amounts made between two dates.

Whilst Lambda functions can be written in one line of code, they are often split over several lines, with a new function per line.                
roles.claimant.select(x => x.ods.name)

The single-line expression above can also be written as:                
roles.claimant        
.select(x => x.ods.name)

 
Function  Example & Explanation

select  

Projects each element of a list into a new form. 

Parameter: 

Selector = a transform function to apply to each element to return all the results as a single value. 

roles.claimant.select(x => x.ods.name)

Returns: 

Brian Edwards,Whit Stillman,John McClaim,James Sample 

 

Iterate through each of the claimants for the work item. 

x represents each individual entry returned by roles.claimant.

Accordingly, x (roles.claimant ) is the parent root and child tags can be used to extract specific data.

In this example, ods.name is used to obtain the name of each claimant.

Select then returns all the results and projects into its new form (a list of claimants presented in a comma-separated format).      
 

selectMany

Projects each element of a sequence to a list and flattens the resulting sequences into one sequence.

Parameter: 

Selector = a transform function to apply to each element to return all the results as a single value. 

workitem
.roles.third-party
.selectMany(x => x.ods.contact.email)
.select(x => x.value)
.join(', ')

This lists out all the emails for third parties on a work item.
We have a list of third parties and each one of those third parties has a list of emails.

The selectMany in this case, will take each list of emails and flatten it out into a single list, then you can do a simple / 'normal' select to get the email value from a single list.

 

This is particularly useful for financial queries:

invoiceDetails.transactions
   .selectMany(x => x.financialTransactions)
   .filter(x => x.segmentCode.code = 'ABC123')

where

Allows you to filter a list/array based on some predicate.

See the filter function below.

workitem.roles.witness     
.where(x => x.ods.name.startsWith('Bob'))     
.select(x => x.ods.name)     
Returns:

All the witness' names that begin with ‘Bob’.

 

any

Returns a true result where any result in the list matches a value. Otherwise, it returns false.

 

form-limtestform.housing-multiple.any(x => x.id = 500000525)

Returns a true result if ANY of the entries selected by the user return a value of 500000525.


This function is ideal for a situation where the user can select multiple entries from an option set, and you need to determine if one specific entry was selected.     
E.g.:      
An option set is presented to the user, asking them to select ALL the Housing Statuses that the client has held in the past 3 years.      
This will check whether the user selected the option set entry for HomeOwner, even if they also selected other statuses, and returns the appropriate text.

 

if (form-limtestform.housing-multiple.any(x => x.id = 500000525) = true) then     
  'The Client has been a HomeOwner'     
else     
  'This Client has not been a HomeOwner'

 

As a second example, imagine trying to find whether the first half of a property's postcode is a match on a code in a list of restricted codes. E.g., is M33 3AB on the list, by matching just ‘M33’.

 

/* The postcodes you want to check for */
var referenceList = [ 'M33', 'L22', 'BD1' ];
 

/*
 The postcode you are checking against.
 In a real implementation, for the client, this is probably:
   var realPostcode = context.client.locations.5002500.address.postcode
*/
var realPostcode = 'L22 2AY';
 

/*
 For each of the reference postcodes ('r'), check whether our real postcode
 starts with 'r'.
*/
var matched = referenceList.Any(r => startsWithIgnoreCase(realPostcode, r));

if (matched) then true else false

 

all

Returns a true result where all results in the list match a value. Otherwise returns false.

workitem.roles.witness

.all(x = x.ods.name.startsWith('Bob'))

Returns a true if ALL of the witness' names start with 'Bob'.

 

Combining a couple of the functions above: getting a list of child properties using where to filter them based on the value of a form builder field (zz-required) and then using all to check whether they all have the same specified requirement-type value.

 

children!q?path=property!1-1000     
.where(x => x.form-property-status.property-zz-required.id = 5023402)     
.all(x => x.form-property-status.property-zz-requirement-type.id = 500000094)

 

filter 

Filters a list of values based on a predicate. filter is an alias of where.

Parameter: 

Predicate = a function to test each element for a particular condition. 

 roles.claimant           
.filter(x => x.legalCapacity.legalCapacity = true)           
.select(x => x.ods.name)

Returns: 

Whit Stillman,John McClaim 

 

Iterate through each of the claimants for the work item and filter by the legal capacity of each claimant.

I.e. x.legalCapacity.legalCapacity = true

Select returns the name of the each relevant participant and transforms the filtered results into a list of claimants presented in a comma-separated format.  

 

join  

Concatenates the elements of a list, using the specified delimiter between each element. 

Parameter: 

Delimiter = String 

e.g. join ('|') 

roles.claimant           
.select(x => x.ods.name)           
.join(', ')

Returns: 

Brian Edwards, Whit Stillman, John McClaim, James Sample

 

Remember: by default, the select function will return multiple values in a transformed list/array. 

join allows each value in the list to be replaced by a different separator – in this example, a comma followed by a space. 

Alternatively,  

.join('|') - returns a pipe-separated list. 

.join('\n ') - returns entries separated by a "new line" or carriage return.     
 

replace 

Replace each instance of a value with another value. 

Parameters: 

oldValue = string 

newValue = string 

roles.claimant           
.select(x => x.ods.name)           
.join(', ')           
.replace(', ', ' and')

Returns: 

Brian Edwards and Whit Stillman and John McClaim and James Sample 

 

+44 replace with 0 

replace(roles.claimant!1.ods.contact.direct-line!1.value,'+44 ','0')       
 

replaceLast 

Replace the last instance of a value with another value. 

Parameters: 

oldValue = string 

newValue = string 

roles.claimant           
.select(x => x.ods.name)           
.join(', ')           
.replaceLast(', ', ' and')

Returns: 

Brian Edwards, Whit Stillman, John McClaim and James Sample 

 

As above, where join produces a list of comma-separated values, the final comma is replaced with the word "and". 

This is typically used in letters and emails to return a list of participants.       
 

orderBy 

Sorts the elements of a list in ascending order. 

roles.claimant      
.select(x => x.ods.name)      
.orderBy()      
.join(', ')

Returns: 

Brian Edwards, James Sample, John McClaim, Whit Stillman 

 

The list of participant names is returned in alphabetical order. 

 

You can also pass in a predicate to order by different properties before returning a set of results. For example using this data:

{      
   "workitem": {      
       "roles": {      
           "witness": [      
               {      
                   "roleOrder": {      
                       "value": 1      
                   },      
                   "ods": {      
                       "name": "Nathan Witness"      
                   }      
               },      
               {      
                   "roleOrder": {      
                       "value": 2      
                   },      
                   "ods": {      
                       "name": "Wendy Witness"      
                   }      
               },      
               {      
                   "roleOrder": {      
                       "value": 3      
                   },      
                   "ods": {      
                       "name": "Andrew Witness"      
                   }      
               }      
           ]      
       }      
   }      
}

Script example 1:

workitem.roles.witness      
.orderBy(x => x.ods.name)      
.select(x => x.ods.name)

This script:

  1. Takes the list of witnesses.
  2. Orders the list alphabetically by name.
  3. Returns only the name property:

[      
   “Andrew Witness”      
   “Nathan Witness”      
   “Wendy Witness”      
]

Script example 2:

workitem.roles.witness      
.orderBy(x => x.roleOrder.value)      
.select(x => x.ods.name)

This script:

  1. Takes the list of witnesses.
  2. Orders the list by role order.
  3. Returns only the name property:

[      
   “Nathan Witness”      
   “Wendy Witness”      
   “Andrew Witness”      
]      
 

orderByDescending 

Sorts the elements of a list in descending order. 

roles.claimant           
.select(x => x.ods.name)           
.orderByDescending()           
.join(', ')

Results: 

Whit Stillman, John McClaim, James Sample, Brian Edwards

 

The list of participant names is returned in reverse alphabetical order.       
 

union  

Joins multiple lists together 

Source1 – first list 

Source2 - second list 

etc 

roles.claimant-counsel           
.select(x => x.ods.name)           
.union(roles.claimant-solicitor.select(x => x.ods.name))           
.union(roles.claimant-litigation-friend.select(x => x.ods.name))

Results: 

Andrew Barrister, Larry Friend, Simon Solicitor 

 

Returns a list of all the participants in any of the three roles that act on behalf of the claimant (counsel, solicitor, litigation friend). 

Note that other functions, such as Join, orderBy, etc., can be applied to the result.       
 

Note that where a union function is called and no results are found, the expression will return null. 

To prevent this, a filter can be placed on the union so that it only returns a result if at least one value exists. 

roles.claimant           
.select(x => x.ods.name)           
.union(roles.defendant.select(x => x.ods.name))           
.union(roles.claimant.select(x => x.ods.name).filter (x => x.ods.name <> null))           
.join(', ')

 

In this example, a filter is applied to the union with a litigation friend participant role - as this role may not always be filled a work item.       
 

sum 

Returns the sum of a list of numeric values. 

workitem.descendants!q?path=invoice-general&closed=true            
.sum(x => x.invoiceDetails.totalAmount)

 

This expression performs a sum of all the 'General' Invoice amounts on the work item.        
 

average 

Returns the average of a list of numeric values. 

workitem.descendants!q?path=invoice-general&closed=true.           
average(x => x.invoiceDetails.totalAmount)

 

This returns the average invoice amount.       
 

min 

Returns the minimum value in a list of values. 

workitem.descendants!q?path=invoice-general&closed=true.           
select(x => x.invoiceDetails.totalAmount)           
.min()

 

This returns the invoice with the least total amount to be paid.       
 

max 

Returns the maximum value in a list of values. 

workitem.descendants!q?path=invoice-general&closed=true.select(x => x.invoiceDetails.totalAmount)           
.max()

 

This returns the invoice with the largest total amount to be paid. 

Litigation Parties

Courts commonly require that all parties in a particular Role (e.g. claimant, defendant) are presented in a list comprising their name, their "ordinal" (e.g. First Claimant, Second Claimant, Third Claimant, etc.) and their address.  

E.g. a list of all Claimants on a Proceeding:

(FIRST) Peter Thompson, Lock Keepers Cottage, Basin Road, Worcester, Worcestershire, WR5 3DA; (SECOND) William Smith, 2 Oxbridge Close, Sale, Greater Manchester, M33 5XJ and (THIRD) Fred Jones, 1 The High Street, Hale Barns, Altrincham, Greater Manchester, WA15 0WU.

Note the final 'and' between the second and third claimants' details.

Further, if there is only one participant in a given role, then courts frequently ask that their ordinal is not presented.  

I.e. if more than one defendant is party to proceedings, use "FIRST Defendant", "SECOND Defendant", etc., otherwise, just present "Defendant".

All of the above can be achieved using the following expression:

if (count(roles.claimant) > 1) then
  roles.claimant.select(x =>  
   '(' + x.roleOrder.ordinal.toUpper() + ') ' + 
   x.ods.name + ', ' + 
   x.locations.5002500.formattedCsv )
  .join('; ')
else
  roles.claimant!1.ods.name + ',' + 
  roles.claimant!1.locations.5002500.formattedCsv

The following expression will return all claimants and their roles on a new line.

if (count(roles.claimant) > 1) then
  roles.claimant.select(x =>  
  x.roleOrder.ordinal.toUpper() + ' Claimant: ' + x.ods.name + ' ')
  .join('\n')
else
  'Claimant' + roles.claimant!1.ods.name + ',' + roles.claimant!1.locations.5002500.formattedCsv

E.g.:

FIRST Claimant: Peter Thompson        
SECOND Claimant: William Smith        
THIRD Claimant: Fred Jones 

Comments

Comments can be added to your expressions. Comments are ignored by ShareDo when the calculated field is run. They are typically used to notate parts of the expression or exclude works in progress from being run whilst testing.

Ensure that the comment begins with a /* and ends with */. The commented script will appear in green text.

A comment can be used over a single line or straddle multiple lines.

User-Defined Script Functions

User-defined script functions are functions which can be written to perform a specific task. Expressions can reference these functions, pass values to them, and receive the resulting data. Storing these functions centrally and calling them only when required ensures uniformity/standardisation and saves rewriting the same script into every expression where a specific task is required.

For example, a function can be scripted to convert a date into a specific format. Rather than write this code into every expression that requires a date to be formatted, a single user-defined function can be called, and the date passed to the function for formatting.       
The Script Functions editor is available via Modeller > Rules > Script Functions.

Script Function Examples

Let's create an example function that replaces the '+44' prefix from any UK telephone number that is passed to it with a 0. We'll use the script function editor to do this.

  1. Use the button to create a new script function. Doing so opens the script function editor.      
  2. Enter a name and a description for the new function in the Field section.       
  3. Parameters determine what values can be passed to the function. E.g., a date to be formatted.      
    Click to add a new parameter.
  4. Enter a Name for the parameter. This is a collective name for the value(s) that will be passed to the function. For example, ParticipantNames or IncidentDate. We will use telephoneNo in our example.      
         
    Leave out the Default Value and Optional options for now. We'll return to them in another example.
  5. Now, write the expression.       
    The expression editor operates similarly to the calculated field expression editor. The main difference is that the parameter name represents the data passed from a calculated field.      
    In this example, if the value of the telephone number held in the telephoneNo parameter contains the value '44 ', then it is replaced with a '0'. The expression we're using is replace(telephoneNo, '+44 ' , '0').      
  6. Save the expression to add it to your list of functions.

Using the new function

To use the function in a calculated field, call the function and pass the field name to be formatted. For example:

RemoveUKCountryCode(roles.claimant!1.ods.contact.direct-line!q?primary=true.value)      

When run, the claimant's direct-dial telephone number will have its prefix of '+44 ' replaced with a '0'.

A more complex example

In this example, we'll look at adding multiple values to the function and use the Default Value field and Optional toggle.

This function removes all country codes, not just the UK's.

As before, create a new function, called RemoveCountryCode, with the following parameters:

  • The first parameter is the telephone number. The Optional toggle is set Off, making the parameter mandatory.
  • The second parameter is the prefix. If this is not passed, then the function will use the default '+44 ' instead (Default Value). This is Optional.

The expression is as follows:

replace(telephoneNo, prefix, '0')

It works like this: take the telephone number and look for the value passed in the prefix parameter. If this is not passed, use the default ‘+44 ‘. Replace the prefix value with a 0.

So, we have a generic function that we can call and either pass any international country code as a parameter or ignore this parameter if the number is UK-based.

In a calculated field, it would be used as follows:

RemoveCountryCode(roles.claimant!1.ods.contact.direct-line!q?primary=true.value)

Deleting Script Functions

To delete a function, hit theicon on the Script Function editor's toolbar. ShareDo will find and list where the function is used.

Hit the green settings icon to view each function usage, and remove it if necessary.

The Function can now be deleted.

Debugging Expressions

Click on either theor buttons to open the Debugger.

Searching

Quick Search 

In the search box at the top left, enter a keyword from the work item that you wish to test your expression against. E.g. part of the client's name, matter reference, participant name, etc. 

Sharedo will present a list of work items that match the keyword, as shown in this example image.

Select the appropriate work item.

Advanced search

Alternatively, use the advanced search option to switch to advanced mode and click > to open the Advanced Search form.

Find the Work Item required.

Manual entry

Finally, the manual entry option allows the unique ID of the work item to be entered. 

Testing Expressions

Click to run the script against the selected work item.

The left-hand console lists any data referenced as part of the expression.

The green panel at the foot of the form returns the expression results.

For example, an expression may format a key date. In this instance, the console would present the date as it is held in ShareDo:

Whereas the results panel would return the actual result of the expression – the formatted date:

Browse

Clicking opens the Tag Browser for the context of your selected work item. 

Copy tag names to the clipboard by navigating to the selected tag and clicking on the copy icon:

You can then paste it into the expression editor.

Save

Use the button to apply any changes made to the expression via the Debugger.

Data Composer Tags in Expressions

When using tags in an expression, you can use the 'local' names for things if you wish.

For example, if I add a new calculated field for the phase of a work type, I can refer to the phase name as either workitem.phase.name or just name.

 

When using data composer tags in expressions, you can move up and down the work type hierarchy:

Operator / Keyword Description
$root Takes you to the base work item.
$parent The parent within the context of type.

Adding a Calculated Field to a List

You can add a calculated field to a list by clicking on the + button underneath the filter for the list. One use for this is adding calculated values to a list or displaying parent information in a list. For more information, see the article Calculated Lists.

Categorisation of Calculated Fields

If you have many calculated fields in your system, you can organise them by creating categories and assigning fields to the categories. 

Use the Category drop-down to assign a field to a category or create a new category.

The following video shows this in practice:

Review

This article introduced you to calculated fields in ShareDo. It showed you how to extract and manipulate ShareDo data using expressions and operators. The article also lists commonly used functions and shows some more advanced functions.