Supporting Person Accounts in Your ISV Solution — Part Two

This is the second installment in this series on supporting Person Accounts in your ISV solution (check out part one here). In this installment, we will talk about how to create a managed package that will support Person Accounts in Financial Services Cloud, and that can be installed in any org, whether Person Accounts have been enabled or not.
 
Part one explained person account-related considerations for managed packages, and a design pattern for ensuring that a managed package can be deployed to an org, even if person accounts have not been enabled (i.e. an org that is using the default business accounts). Now lets see how that design pattern is implemented in practice.

Scenario

Consider an external system with customer birthdates. We need to make an on-demand API call to get an individual’s birthdate from that system, and then save the result in the Salesforce field “Account.PersonBirthDate.” We will ensure that code is written in a way that doesn’t require Person Accounts to have been enabled in advance. 
 
For this use case, we will create a Lightning component and embed it on the Account record page’s sidebar. This component will display a button if the record is a PersonAccount. When the button is pressed, and if the record is a Person Account, then information will be fetched with an API callout and the record updated.
 
With that, let’s get started.

Development

The following Apex class will return a birthdate when called.

Note: You should always implement CRUD/FLS check for SOQL/DML in Apex. Security should be integrated into the process from the beginning. Following code is for demo purposes only.

public with sharing class AccountAPI {
@AuraEnabled
public static boolean isFSCPersonAccountEnabled(){
//Check if FSC Person Account is enabled
//Requires install of FSC
FinServ__UsePersonAccount__c fscpa = FinServ__UsePersonAccount__c.getInstance('Use Person Account');
return (fscpa != null)?fscpa.FinServ__Enable__c:false;
}
@AuraEnabled
public static APIWrapper fetchAccountInfo(Id AccountId){
APIWrapper apiw = new APIWrapper();


//Confirm that ID is correct
AccountId = String.escapeSingleQuotes(AccountId);
String query = 'Select Id, PersonBirthDate from Account where Id=:AccountId';
try{
sObject a = Database.query(query);
//Make PRETEND API call
//FetchAccountInfo(AccountId)
apiw.BirthDate = date.newInstance(1990, 11, 21);
}catch(Exception e){

}

return apiw;
}
//Wrapper class to return Data
public class APIWrapper{
@AuraEnabled
public Date BirthDate;
}
}

Lightning component to use above Apex class, fetch BirthDate and save record.

<aura:component controller="AccountAPI" implements="flexipage:availableForRecordHome,force:hasRecordId" access="global" >
<aura:attribute name="account" type="Object"/>
<aura:attribute name="simpleAccount" type="Object"/>
<aura:attribute name="accountError" type="String"/>
<force:recordData aura:id="accountRecordLoader"
recordId="{!v.recordId}"
layoutType="FULL"
targetRecord="{!v.account}"
targetFields="{!v.simpleAccount}"
targetError="{!v.accountError}"
mode="EDIT"
/>

<aura:attribute name="isFSCPersonAccountEnabled" type="boolean" default="false" />
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<!-- If no loading errors then proceed -->
<aura:if isTrue="{!empty(v.accountError)}">
<aura:if isTrue="{!v.isFSCPersonAccountEnabled}">
<aura:if isTrue="{!v.simpleAccount.IsPersonAccount}">
<lightning:button label="Fetch &amp; Save Data" onclick="{!c.fetchAndSave}" class="slds-m-top_medium" />
<aura:set attribute="else">
This is not a Person Account
</aura:set>
</aura:if>
<aura:set attribute="else">
This is not a Person Account
</aura:set>
</aura:if>
</aura:if>
</aura:component>
({
doInit: function(component, event, helper) {
//Check if FSC Person account is enabled from custom settings
var action = component.get("c.isFSCPersonAccountEnabled");
action.setCallback(this, function(response){
if(component.isValid() && response !== null && response.getState() == 'SUCCESS'){
//Store if FSC Person Account is Enabled
component.set("v.isFSCPersonAccountEnabled", response.getReturnValue());
}
});

$A.enqueueAction(action);
},
fetchAndSave: function(component, event, helper){
//Fetch BirthDate via API to update the record
var action = component.get("c.fetchAccountInfo");
var acc = component.get('v.simpleAccount');
action.setParams({"AccountId": acc.Id});
        action.setCallback(this, function(response){
if(component.isValid() && response !== null && response.getState() == 'SUCCESS'){
var obj = response.getReturnValue();
component.set("v.simpleAccount.PersonBirthdate ", obj.BirthDate);
component.find("accountRecordLoader").saveRecord(function(saveResult) {
if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
var toastEvent = $A.get("e.force:showToast");
toastEvent.setParams({
title: "Success!",
message: "BirthDate Updated!",
type: "success"
});
toastEvent.fire();
}
else if (saveResult.state === "INCOMPLETE") {
console.log("User is offline, device doesn't support drafts.");
}
else if (saveResult.state === "ERROR") {
console.log('Problem saving contact, error: ' +
JSON.stringify(saveResult.error));
}
else {
console.log('Unknown problem, state: ' + saveResult.state +
', error: ' + JSON.stringify(saveResult.error));
}
})
}
});

$A.enqueueAction(action);
}
})

Packaging

The next step, after development, is to package the solution. Packaging follows the same standardized process that you know and love. I have packaged the above code and installed it in orgs with and without Person Accounts enabled. There were no installation errors and the code worked without any issues. 
 
You can test it by installing this package from https://login.salesforce.com/packaging/installPackage.apexp?p0=04t1I000003cqto

Security Review

The standard Security Review Process should be followed. Make sure to add CRUD/FLS checks (at the very least) in the code above. As it is, the package above will fail Security Review because I haven’t implemented any CRUD/FLS checks when running the query.

Conclusion

With this, you should be able to developer and package your functionality to support Person and Business Account in the same package.