2

I've grappled with this one for a long time, and I am almost sure it's a gap in the platform. The problem I have is that I need a way to find out all the fields that can be tracked. That is to say, all of the fields that would be visible in the setup menu if you went to an object and tried to track fields.

The metadata api seemed to be a logical contender for this purpose. And it was! It was working swimmingly for standard objects. Even standard objects with custom fields were a-ok. However, if you read (or retrieve) the CustomObject metadata for a custom object OR the CustomField metadata from a field on a custom object, the trackHistory value does not come back or comes back as null.

So for standard objects, I am able to tell the total amount of trackable fields by reading metadata for all the fields on the object, and noticing which values come back as true or false rather than null.

But since I am not able to get the trackHistory value back from fields on custom objects, I can't tell which fields on the custom objects can be tracked.

I am able to tell which fields ARE tracked on the custom objects by heading to the FieldDefinition Tooling API table. There is a field on that table titled IsFieldHistoryTracked. That field actually tells you if the field is tracked or not (contrary to the docs, which SAY that it is supposed to show if "the field’s history can be tracked", but i digress).

HOWEVER, just knowing that the field is tracked is not enough, as all of the fields that can or cannot be tracked have a true / false value in this field.

So, does anyone know of a way out of this for me? Is there any real way to figure out which fields can and can not be tracked programmatically? Do I have to scrape the menu screen (60% joking).

Any and all help is much appreciated.

  • Could you please explain the use case you are trying to achieve by finding out if a field can be tracked or not. I believe trackHistory is for specifying whether to track history or not on the field via metadata API. Related link: salesforce.stackexchange.com/questions/146008/… – Prudhvi Konda Sep 13 at 22:50
  • 1
    This might not be the best idea but try creating a custom object with different field types that Salesforce allows and see which data types can be tracked and which can't be. Based on this info, and based on the field's data type using the metadata, I think you can achieve what you want. Not a straight forward approach and involves maintenance but achieves what you need. – Prudhvi Konda Sep 13 at 22:54
  • I have come to the same conclusion, Prudhvi, I think deducing the trackable custom fields through the FieldDefinition table is the only way possible to achieve the goal of finding out which fields can be tracked. I also found this help article which outlines which types of custom fields can theoretically be tracked. help.salesforce.com/… I will write up an answer unless you would prefer to, which states that. – Toby Curtis Sep 13 at 23:16
  • ?? Metadata API returns <trackHistory>true</trackHistory> on custom object fields for me – cropredy Sep 13 at 23:36
  • Hmm, that is strange. I was doing both reads through the financial force dev apex-mdapi classes as well as retrieves through workbench, and on every custom object and custom field that I retrieved or read which had a tracked field came back with no trackHistory value. So At the best we can say it's inconsistent. – Toby Curtis Sep 14 at 1:25
1

Fields that are tracked using the Metadata API

Using Workbench, I used this package:

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>SuperOrder__c</members>
        <name>CustomObject</name>
    </types>
    <version>46.0</version>
</Package>

where SuperOrder__c is a custom object in my org

and in the retrieved zip file, I get:

...
<fields>
        <fullName>Cancelled_Order_Count__c</fullName>
        <description>calculated by dlrs</description>
        <externalId>false</externalId>
        <inlineHelpText># of child Orders where Status = Cancelled</inlineHelpText>
        <label>Cancelled Order Count</label>
        <precision>18</precision>
        <required>false</required>
        <scale>0</scale>
        <trackHistory>true</trackHistory>  <-- LOOK HERE!
        <trackTrending>false</trackTrending>
        <type>Number</type>
        <unique>false</unique>
    </fields>
...

Now, if your version of the fflib Metadata API wrapper is too old (the current version on GitHub is V42.0), you may need to patch the installed version using the How to create your own MetadataService.cls instructions

Fields that are trackable

  • The Metadata API will return fields that are not trackable with <trackHistory>false</trackHistory>
  • But a trackable field that is not tracked will also return <trackHistory>false</trackHistory>

So, how to differentiate?

  • If the custom field is a Formula, by definition, it is not trackable then the metadata will include <formula>...</formula>

In addition, from the doc:

You can’t track the following fields:

  • Formula, roll-up summary, or auto-number fields
  • Created By and Last Modified By
  • Fields that have the AI Prediction checkbox selected
  • Expected Revenue field on opportunities
  • Master Solution Title or the Master Solution Details fields on solutions; these fields display only for translated solutions in organizations with multilingual solutions enable
  • So I just gave it another go, and in 3 seperate orgs (1 scratch org, 1 enterprise org created from the environment hub, and 1 developer edition org), I created a custom object, created a custom field on that object, and tracked it. Then I retrieved the CustomObject metadata through workbench using your package xml above (w/ a changed object name), and trackHistory was never in the results. So I'm not saying I don't believe you got those results from a metadata retrieve, but I am saying that as an ISV exposed to many environments, I need to use the most resilient method to get this information. – Toby Curtis Sep 15 at 7:01
  • @TobyCurtis - your results are certainly mystifying. Doc says if you are at API version 30.0 or higher, you should get <trackHistory> and you obviously do for standard objects. I assume your custom object is NOT an external object nor a Platform Event object.. – cropredy Sep 15 at 21:13
  • That is correct, it is just a normal __c custom object that I created quickly from the object manager. I am going to record a video of the process which I can link here later today to add more clarity to exactly what I am doing. – Toby Curtis Sep 16 at 15:22
  • So I ran another test in a different org that had Field Audit Trail and it came back with the correct trackHistory value... so I am not sure if it was related to the org having FAT or what, but then I went back to some other testing orgs, and it is still a problem using the same method. So I think I'll have to stick with the combined api method for my use case. Thanks for the deep dive, @cropredy , really appreciate it. – Toby Curtis Sep 17 at 17:31
  • 1
    when you created the custom object, in the lower left, you did not check Track Field History.(BTW - nice video - Oscar nominee for sure) – cropredy Sep 17 at 20:30
1

So along with @Prudhvi Konda, I came to the conclusion that the best solution would be to deduce the custom fields by which fields can be tracked or not.

So my final method to find out which fields were trackable was split into 2 approaches: Standard fields and Custom fields.

Standard Fields Use the Metadata API to read the CustomField metadata for all standard fields. Record which fields have a trackHistory value of true or false. Those are all the trackable standard fields (and their tracked values).

Custom Fields Use the FieldDefinition Tooling API object to check all fields on Custom objects. You are looking for any field from a custom object that meets the following criteria:

  • Data Type is not a formula, auto number, or roll up summary
  • Field API name is not in this list: 'Id', 'IsDeleted', 'CreatedDate', 'CreatedById', 'LastModifiedDate', 'LastModifiedById', 'SystemModstamp', 'UserRecordAccessId', 'RecordVisibilityId'

After you have filtered out fields that meet the above criteria, you can check the IsFieldHistoryTracked value to find out if the field is tracked or not

Combining these two data sets gives you the most complete programmatic picture you can paint of trackable fields in a general org.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.