What/who.(RecordType.Id vs RecordTypeId) SF Bug

apexbugunit-test

Task[] tasks = [SELECT Id FROM Task WHERE What.RecordType.Id = :AccountsHelper.getPersonAccountRecordType().Id LIMIT 1];

System.debug('tasks size : ' + tasks.size());

Task[] tasks2 = [SELECT Id FROM Task WHERE What.RecordTypeId = :AccountsHelper.getPersonAccountRecordType().Id LIMIT 1];

System.debug('tasks2 size : ' + tasks2.size());

DEBUG|tasks size : 1

DEBUG|tasks2 size : 0

Looks like when using polymorphic fields in unit tests RecordTypeId is not working as expected.

When I used the same query with RecordTypeId outside unit tests in:

  • dev console query
  • salesforce inspector query
  • created selector and invoked in dev console

All works as expected.

I bumped API version to 52 and 53 and still the same behaviour in unit test.

Anybody else experienced the same?

Best Answer

Update:

This is now logged as W-10188438. At this time, there is no ETA for a resolution. The basic problem, as stated by support, is:

The named entity usecase is missed for person account in implementation during SOQL query execution. In our internal code base, Person account id is added to the query filter only when queried based on Account and Contact entities.

This means that, for the foreseeable future, developers will need to be aware that this problem exists, and what the solution to the problem is. Fortunately, the workaround is simple and relatively limited in scope.


I wrote some unit tests:

@isTest public class q359389 {
    static final Boolean IS_PERSON = true, IS_BUSINESS = false;
    static Account createAccountAndTask(Boolean isPerson) {
        RecordType rt = [SELECT Id FROM RecordType WHERE IsPersonType=:isPerson AND sObjectType='Account' LIMIT 1];
        Account a = new Account(RecordTypeId=rt.Id);
        a.put(isPerson?Account.LastName:Account.Name,'Test');
        insert a;
        Task t = new Task(WhatId=a.Id, Subject='Test', Status='Not Started',Priority='Normal');
        insert t;
        return a;
    }
    enum FILTER_BY { ACCOUNT, WHAT }
    static void doAssert(Account a, FILTER_BY filter) {
        upsert a Account.Id;
        switch on filter {
            when ACCOUNT {
                System.assertEquals(1, [SELECT COUNT() FROM Task WHERE Account.RecordType.Id = :a.RecordTypeId],'Account.RecordType.Id filter failed');
                System.assertEquals(1, [SELECT COUNT() FROM Task WHERE Account.RecordTypeId = :a.RecordTypeId],'Account.RecordTypeId filter failed');
            }
            when WHAT {
                System.assertEquals(1, [SELECT COUNT() FROM Task WHERE What.RecordType.Id = :a.RecordTypeId],'What.RecordType.Id filter failed');
                System.assertEquals(1, [SELECT COUNT() FROM Task WHERE What.RecordTypeId = :a.RecordTypeId],'What.RecordTypeId filter failed');
            }
        }
    }
    @isTest static void filterPersonWithWhatIdByWhatId() {
        doAssert(createAccountAndTask(IS_PERSON), FILTER_BY.WHAT);
    }
    @isTest static void filterPersonWithWhatIdByAccountId() {
        doAssert(createAccountAndTask(IS_PERSON), FILTER_BY.ACCOUNT);
    }
    @isTest static void filterBusinessWithWhatIdByWhatId() {
        doAssert(createAccountAndTask(IS_BUSINESS), FILTER_BY.WHAT);
    }
    @isTest static void filterBusinessWithWhatIdByAccountId() {
        doAssert(createAccountAndTask(IS_BUSINESS), FILTER_BY.ACCOUNT);
    }
    @isTest static void testPersonFieldsDirect() {
        Account a = createAccountAndTask(IS_PERSON);
        Task t = [SELECT What.RecordTypeId FROM Task];
        System.assertNotEquals(null, t.What.RecordTypeId, 'What.RecordTypeId was null');
    }
    @isTest static void testPersonFieldsIndirect() {
        Account a = createAccountAndTask(IS_PERSON);
        Task t = [SELECT What.RecordType.Id FROM Task];
        System.assertNotEquals(null, t.What.RecordType.Id, 'What.RecordType.Id was null');
    }
    @isTest static void testBusinessFields() {
        Account a = createAccountAndTask(IS_BUSINESS);
        Task t = [SELECT What.RecordType.Id, What.RecordTypeId FROM Task];
        System.assertNotEquals(null, t.What.RecordType.Id, 'What.RecordType.Id was null');
        System.assertNotEquals(null, t.What.RecordTypeId, 'What.RecordTypeId was null');
    }
}

And found that this only fails for What.RecordTypeId, and only if the Account is a Person Account. In other words, this is definitely a bug, and should be reported. This test is portable to any org that has Person Accounts enabled and minimal validation/automation/etc. You can even run this in a Scratch Org with the feature enabled.

Edited: Also, selecting What.RecordTypeId or What.RecordType.Id results in a null value, so there's some additional problems with Record Types and Person Accounts. I have updated the tests accordingly.