Add “Deployment Status” to the check-list…

Here is another item to add to the check-list (see Always double check that managed packages are deployed for some others) when objects or fields are not being displayed.

Our problem was that while the application worked fine through the development process and when tested in the production org via a “System Administrator” user, for any other profile user, the fields of two of the objects did not display at all in their Visualforce pages.

In this case the cause was that the “Deployment Status” of the objects had been accidentally left as “In Development”. This status is represented as <deploymentStatus>InDevelopment</deploymentStatus> in the .object file and appears in the development web UI like this:

Knowing what to look for in the help, it is easy to find the explanation:

Choose “In Development” as the Deployment Status when first creating your custom object to hide it from users while you are designing and testing it. Making the status “In Development” hides the custom object tab, search results, related lists, and report data types from all users except those with the “Customize Application” permission.

Fair enough functionality if you are modifying a live environment but in any approach where the software is developed separately from the live environment it is not useful and in this case was harmful. So always sticking to “Deployed” seems the way to go. And of course testing earlier under the actual profile(s) that will be used…

Advertisements

Presenting the Account hierarchy in an apex:selectList

PS As well as usability issues when the number of Accounts gets into the hundreds, the algorithm used here is unfortunately exponentially expensive and so at around 500 Accounts the 200,000 script statement governor limit is likely to be hit. So I have replaced this algorithm with a linearly expensive one (not shown here) that can handle many thousands of Accounts.

Accounts can have a parent Account reference set so that a hierarchy is formed and this hierarchy can be displayed using the “View Hierarchy” link. With a bit of code the hierarchy (and similar hierarchies) can be represented in a Visualforce select list that it is visible to the user at the time they make a selection:

This sort of presentation is obviously not appropriate if there are a large number of Accounts.

Here is an implementation. Most of the work is done by an inner class called Node that represents the hierarchy. The Accounts are queried in alphabetic order, then (iteratively) added according to their ParentId into the Node hierarchy, then converted into a list of items for presentation in the select list:

public with sharing class MyController {
    public List<SelectOption> accountOptions {
        get {
            Node root = new Node();
            List<Account> nextPass = [select Id, ParentId, Name from Account order by Name];
            while (!nextPass.isEmpty()) {
                List<Account> currentPass = nextPass;
                nextPass = new List<Account>();
                for (Account account : currentPass) {
                    if (!root.add(account)) {
                        nextPass.add(account);
                    }
                }
            }
            List<SelectOption> options = new List<SelectOption>();
            options.add(new SelectOption('', '--None--'));
            root.buildOptions(options);
            return options;
        }
    }
    private class Node {
        private Account account;
        private List<Node> children = new List<Node>();
        private Integer depth = -1;
        public Node() {
        }
        private Node(Account account, Integer depth) {
            this.account = account;
            this.depth = depth;
        }
        public Boolean add(Account candidate) {
            return add(candidate, 0);
        }
        private Boolean add(Account candidate, Integer depth) {
            if ((isRoot() && candidate.ParentId == null) || (!isRoot() && candidate.ParentId == account.Id)) {
                children.add(new Node(candidate, depth));
                return true;
            } else {
                for (Node child : children) {
                    if (child.add(candidate, depth + 1)) {
                        return true;
                    }
                }
            }
            return false;
        }
        private Boolean isRoot() {
            return account == null;
        }
        public void buildOptions(List<SelectOption> options) {
            if (account != null) {
                options.add(createSelectOption());
            }
            for (Node child : children) {
                child.buildOptions(options);
            }
        }
        private SelectOption createSelectOption() {
            String label = '';
            for (Integer i = 0; i < depth; i++) {
                label += '    ';
            }
            label += account.Name;
            SelectOption option = new SelectOption(account.Id, label);
            option.setEscapeItem(false);
            return option;
        }
    }
}

To present the list:

<apex:selectList value="{!accountId}" size="1">
    <apex:selectOptions value="{!accountOptions}"/>
</apex:selectList>

And here is a basic test for the code:

@isTest
private class MyControllerTest {
    @isTest
    static void testAccountTree() {
        Long t = System.currentTimeMillis();
        Account child = new Account(Name = 'Child-' + t);
        insert child;
        Account parent = new Account(Name = 'Parent-' + t);
        insert parent;
        Account grandChild = new Account(Name = 'GrandChild-' + t);
        insert grandChild;
        child.ParentId = parent.Id;
        grandChild.ParentId = child.Id;
        update new Account[] {child, grandChild};
        
        MyController controller = new MyController();
        List<SelectOption> options = controller.accountOptions;
        
        Integer parentPosition = null;
        Integer childPosition = null;
        Integer grandChildPosition = null;
        for (Integer i = 0; i < options.size(); i++) {
            SelectOption option = options[i];
            if (isMatch(option, parent)) {
                parentPosition = i;
            }
            if (isMatch(option, child)) {
                childPosition = i;
            }
            if (isMatch(option, grandChild)) {
                grandChildPosition = i;
            }
        }
        // All there
        System.assertNotEquals(null, parentPosition);
        System.assertNotEquals(null, childPosition);
        System.assertNotEquals(null, grandChildPosition);
        // In right order
        System.assert(childPosition > parentPosition);
        System.assert(grandChildPosition > childPosition);
    }
}